1
0
Fork 0
mirror of https://github.com/nix-community/home-manager.git synced 2025-11-08 11:36:05 +01:00

treewide: reformat nixfmt-rfc-style

Reformat repository using new nixfmt-rfc-style.
This commit is contained in:
Austin Horstman 2025-04-07 16:11:29 -05:00
parent 5df48c4255
commit cba2f9ce95
1051 changed files with 37028 additions and 26594 deletions

View file

@ -1,4 +1,6 @@
{ pkgs ? import <nixpkgs> { } }:
{
pkgs ? import <nixpkgs> { },
}:
let
path = builtins.path {
@ -6,22 +8,26 @@ let
name = "home-manager-source";
};
in rec {
docs = let releaseInfo = pkgs.lib.importJSON ./release.json;
in with import ./docs {
inherit pkgs;
inherit (releaseInfo) release isReleaseBranch;
}; {
in
rec {
docs =
let
releaseInfo = pkgs.lib.importJSON ./release.json;
in
with import ./docs {
inherit pkgs;
inherit (releaseInfo) release isReleaseBranch;
};
{
inherit manPages jsonModuleMaintainers;
inherit (manual) html htmlOpenTool;
inherit (options) json;
};
inherit manPages jsonModuleMaintainers;
inherit (manual) html htmlOpenTool;
inherit (options) json;
};
home-manager = pkgs.callPackage ./home-manager { inherit path; };
install =
pkgs.callPackage ./home-manager/install.nix { inherit home-manager; };
install = pkgs.callPackage ./home-manager/install.nix { inherit home-manager; };
nixos = import ./nixos;
lib = import ./lib { inherit (pkgs) lib; };

View file

@ -1,9 +1,12 @@
{ pkgs
{
pkgs,
# Note, this should be "the standard library" + HM extensions.
, lib ? import ../modules/lib/stdlib-extended.nix pkgs.lib
# Note, this should be "the standard library" + HM extensions.
lib ? import ../modules/lib/stdlib-extended.nix pkgs.lib,
, release, isReleaseBranch }:
release,
isReleaseBranch,
}:
let
@ -19,86 +22,124 @@ let
# Caveat: even if the package is reached by a different means, the
# path above will be shown and not e.g.
# `${config.services.foo.package}`.
scrubDerivations = prefixPath: attrs:
scrubDerivations =
prefixPath: attrs:
let
scrubDerivation = name: value:
let pkgAttrName = prefixPath + "." + name;
in if lib.isAttrs value then
scrubDerivation =
name: value:
let
pkgAttrName = prefixPath + "." + name;
in
if lib.isAttrs value then
scrubDerivations pkgAttrName value
// lib.optionalAttrs (lib.isDerivation value) {
outPath = "\${${pkgAttrName}}";
}
else
value;
in lib.mapAttrs scrubDerivation attrs;
in
lib.mapAttrs scrubDerivation attrs;
# Make sure the used package is scrubbed to avoid actually
# instantiating derivations.
scrubbedPkgsModule = {
imports = [{
_module.args = {
pkgs = lib.mkForce (scrubDerivations "pkgs" pkgs);
pkgs_i686 = lib.mkForce { };
};
}];
imports = [
{
_module.args = {
pkgs = lib.mkForce (scrubDerivations "pkgs" pkgs);
pkgs_i686 = lib.mkForce { };
};
}
];
};
dontCheckDefinitions = { _module.check = false; };
dontCheckDefinitions = {
_module.check = false;
};
gitHubDeclaration = user: repo: subpath:
let urlRef = if isReleaseBranch then "release-${release}" else "master";
in {
gitHubDeclaration =
user: repo: subpath:
let
urlRef = if isReleaseBranch then "release-${release}" else "master";
in
{
url = "https://github.com/${user}/${repo}/blob/${urlRef}/${subpath}";
name = "<${repo}/${subpath}>";
};
hmPath = toString ./..;
buildOptionsDocs = args@{ modules, includeModuleSystemOptions ? true, ... }:
buildOptionsDocs =
args@{
modules,
includeModuleSystemOptions ? true,
...
}:
let
options = (lib.evalModules {
inherit modules;
class = "homeManager";
}).options;
in pkgs.buildPackages.nixosOptionsDoc ({
options = if includeModuleSystemOptions then
options
else
builtins.removeAttrs options [ "_module" ];
transformOptions = opt:
opt // {
# Clean up declaration sites to not refer to the Home Manager
# source tree.
declarations = map (decl:
if lib.hasPrefix hmPath (toString decl) then
gitHubDeclaration "nix-community" "home-manager"
(lib.removePrefix "/" (lib.removePrefix hmPath (toString decl)))
else if decl == "lib/modules.nix" then
# TODO: handle this in a better way (may require upstream
# changes to nixpkgs)
gitHubDeclaration "NixOS" "nixpkgs" decl
else
decl) opt.declarations;
};
} // builtins.removeAttrs args [ "modules" "includeModuleSystemOptions" ]);
options =
(lib.evalModules {
inherit modules;
class = "homeManager";
}).options;
in
pkgs.buildPackages.nixosOptionsDoc (
{
options =
if includeModuleSystemOptions then options else builtins.removeAttrs options [ "_module" ];
transformOptions =
opt:
opt
// {
# Clean up declaration sites to not refer to the Home Manager
# source tree.
declarations = map (
decl:
if lib.hasPrefix hmPath (toString decl) then
gitHubDeclaration "nix-community" "home-manager" (
lib.removePrefix "/" (lib.removePrefix hmPath (toString decl))
)
else if decl == "lib/modules.nix" then
# TODO: handle this in a better way (may require upstream
# changes to nixpkgs)
gitHubDeclaration "NixOS" "nixpkgs" decl
else
decl
) opt.declarations;
};
}
// builtins.removeAttrs args [
"modules"
"includeModuleSystemOptions"
]
);
hmOptionsDocs = buildOptionsDocs {
modules = import ../modules/modules.nix {
inherit lib pkgs;
check = false;
} ++ [ scrubbedPkgsModule ];
modules =
import ../modules/modules.nix {
inherit lib pkgs;
check = false;
}
++ [ scrubbedPkgsModule ];
variablelistId = "home-manager-options";
};
nixosOptionsDocs = buildOptionsDocs {
modules = [ ../nixos scrubbedPkgsModule dontCheckDefinitions ];
modules = [
../nixos
scrubbedPkgsModule
dontCheckDefinitions
];
includeModuleSystemOptions = false;
variablelistId = "nixos-options";
optionIdPrefix = "nixos-opt-";
};
nixDarwinOptionsDocs = buildOptionsDocs {
modules = [ ../nix-darwin scrubbedPkgsModule dontCheckDefinitions ];
modules = [
../nix-darwin
scrubbedPkgsModule
dontCheckDefinitions
];
includeModuleSystemOptions = false;
variablelistId = "nix-darwin-options";
optionIdPrefix = "nix-darwin-opt-";
@ -108,22 +149,26 @@ let
revision = "release-${release-config.release}";
# Generate the `man home-configuration.nix` package
home-configuration-manual =
pkgs.runCommand "home-configuration-reference-manpage" {
nativeBuildInputs =
[ pkgs.buildPackages.installShellFiles pkgs.nixos-render-docs ];
allowedReferences = [ "out" ];
} ''
# Generate manpages.
mkdir -p $out/share/man/man5
mkdir -p $out/share/man/man1
nixos-render-docs -j $NIX_BUILD_CORES options manpage \
--revision ${revision} \
--header ${./home-configuration-nix-header.5} \
--footer ${./home-configuration-nix-footer.5} \
${hmOptionsDocs.optionsJSON}/share/doc/nixos/options.json \
$out/share/man/man5/home-configuration.nix.5
cp ${./home-manager.1} $out/share/man/man1/home-manager.1
'';
pkgs.runCommand "home-configuration-reference-manpage"
{
nativeBuildInputs = [
pkgs.buildPackages.installShellFiles
pkgs.nixos-render-docs
];
allowedReferences = [ "out" ];
}
''
# Generate manpages.
mkdir -p $out/share/man/man5
mkdir -p $out/share/man/man1
nixos-render-docs -j $NIX_BUILD_CORES options manpage \
--revision ${revision} \
--header ${./home-configuration-nix-header.5} \
--footer ${./home-configuration-nix-footer.5} \
${hmOptionsDocs.optionsJSON}/share/doc/nixos/options.json \
$out/share/man/man5/home-configuration.nix.5
cp ${./home-manager.1} $out/share/man/man1/home-manager.1
'';
# Generate the HTML manual pages
home-manager-manual = pkgs.callPackage ./home-manager-manual.nix {
home-manager-options = {
@ -135,22 +180,26 @@ let
};
html = home-manager-manual;
htmlOpenTool = pkgs.callPackage ./html-open-tool.nix { } { inherit html; };
in {
in
{
options = {
# TODO: Use `hmOptionsDocs.optionsJSON` directly once upstream
# `nixosOptionsDoc` is more customizable.
json = pkgs.runCommand "options.json" {
meta.description = "List of Home Manager options in JSON format";
} ''
mkdir -p $out/{share/doc,nix-support}
cp -a ${hmOptionsDocs.optionsJSON}/share/doc/nixos $out/share/doc/home-manager
substitute \
${hmOptionsDocs.optionsJSON}/nix-support/hydra-build-products \
$out/nix-support/hydra-build-products \
--replace-fail \
'${hmOptionsDocs.optionsJSON}/share/doc/nixos' \
"$out/share/doc/home-manager"
'';
json =
pkgs.runCommand "options.json"
{
meta.description = "List of Home Manager options in JSON format";
}
''
mkdir -p $out/{share/doc,nix-support}
cp -a ${hmOptionsDocs.optionsJSON}/share/doc/nixos $out/share/doc/home-manager
substitute \
${hmOptionsDocs.optionsJSON}/nix-support/hydra-build-products \
$out/nix-support/hydra-build-products \
--replace-fail \
'${hmOptionsDocs.optionsJSON}/share/doc/nixos' \
"$out/share/doc/home-manager"
'';
};
manPages = home-configuration-manual;
@ -158,13 +207,18 @@ in {
manual = { inherit html htmlOpenTool; };
# Unstable, mainly for CI.
jsonModuleMaintainers = pkgs.writeText "hm-module-maintainers.json" (let
result = lib.evalModules {
modules = import ../modules/modules.nix {
inherit lib pkgs;
check = false;
} ++ [ scrubbedPkgsModule ];
class = "homeManager";
};
in builtins.toJSON result.config.meta.maintainers);
jsonModuleMaintainers = pkgs.writeText "hm-module-maintainers.json" (
let
result = lib.evalModules {
modules =
import ../modules/modules.nix {
inherit lib pkgs;
check = false;
}
++ [ scrubbedPkgsModule ];
class = "homeManager";
};
in
builtins.toJSON result.config.meta.maintainers
);
}

View file

@ -9,7 +9,12 @@
};
};
outputs = { self, nixpkgs, scss-reset }:
outputs =
{
self,
nixpkgs,
scss-reset,
}:
let
supportedSystems = [
"aarch64-darwin"
@ -28,7 +33,12 @@
p-build = pkgs.writeShellScriptBin "p-build" ''
set -euo pipefail
export PATH=${lib.makeBinPath [ pkgs.coreutils pkgs.rsass ]}
export PATH=${
lib.makeBinPath [
pkgs.coreutils
pkgs.rsass
]
}
tmpfile=$(mktemp -d)
trap "rm -r $tmpfile" EXIT
@ -42,20 +52,25 @@
};
releaseInfo = lib.importJSON ../release.json;
in {
devShells = forAllSystems (system:
in
{
devShells = forAllSystems (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
fpkgs = flakePkgs pkgs;
in {
in
{
default = pkgs.mkShell {
name = "hm-docs";
packages = [ fpkgs.p-build ];
};
});
}
);
# Expose the docs outputs
packages = forAllSystems (system:
packages = forAllSystems (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
docs = import ./default.nix {
@ -63,10 +78,12 @@
release = releaseInfo.release;
isReleaseBranch = releaseInfo.isReleaseBranch;
};
in {
in
{
inherit (docs) manPages jsonModuleMaintainers;
inherit (docs.manual) html htmlOpenTool;
inherit (docs.options) json;
});
}
);
};
}

View file

@ -1,7 +1,15 @@
{ stdenv, lib, documentation-highlighter, revision, home-manager-options
, nixos-render-docs }:
let outputPath = "share/doc/home-manager";
in stdenv.mkDerivation {
{
stdenv,
lib,
documentation-highlighter,
revision,
home-manager-options,
nixos-render-docs,
}:
let
outputPath = "share/doc/home-manager";
in
stdenv.mkDerivation {
name = "home-manager-manual";
nativeBuildInputs = [ nixos-render-docs ];
@ -61,5 +69,7 @@ in stdenv.mkDerivation {
passthru = { inherit home-manager-options; };
meta = { maintainers = [ lib.maintainers.considerate ]; };
meta = {
maintainers = [ lib.maintainers.considerate ];
};
}

View file

@ -1,6 +1,14 @@
{ writeShellScriptBin, makeDesktopItem, symlinkJoin }:
{ html, pathName ? "home-manager", projectName ? pathName
, name ? "${pathName}-help" }:
{
writeShellScriptBin,
makeDesktopItem,
symlinkJoin,
}:
{
html,
pathName ? "home-manager",
projectName ? pathName,
name ? "${pathName}-help",
}:
let
helpScript = writeShellScriptBin name ''
set -euo pipefail
@ -30,7 +38,11 @@ let
exec = "${helpScript}/bin/${name}";
categories = [ "System" ];
};
in symlinkJoin {
in
symlinkJoin {
inherit name;
paths = [ helpScript desktopItem ];
paths = [
helpScript
desktopItem
];
}

View file

@ -1,6 +1,18 @@
{ lib, flake-parts-lib, moduleLocation, ... }:
let inherit (lib) toString mapAttrs mkOption types;
in {
{
lib,
flake-parts-lib,
moduleLocation,
...
}:
let
inherit (lib)
toString
mapAttrs
mkOption
types
;
in
{
options = {
flake = flake-parts-lib.mkSubmoduleOptions {
homeConfigurations = mkOption {
@ -17,11 +29,13 @@ in {
homeModules = mkOption {
type = types.lazyAttrsOf types.deferredModule;
default = { };
apply = mapAttrs (k: v: {
_class = "homeManager";
_file = "${toString moduleLocation}#homeModules.${k}";
imports = [ v ];
});
apply = mapAttrs (
k: v: {
_class = "homeManager";
_file = "${toString moduleLocation}#homeModules.${k}";
imports = [ v ];
}
);
description = ''
Home Manager modules.

View file

@ -10,7 +10,13 @@
};
};
outputs = { self, nixpkgs, treefmt-nix, ... }:
outputs =
{
self,
nixpkgs,
treefmt-nix,
...
}:
{
nixosModules = rec {
home-manager = ./nixos;
@ -44,42 +50,50 @@
};
lib = import ./lib { inherit (nixpkgs) lib; };
} // (let
forAllSystems = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed;
}
// (
let
forAllSystems = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed;
treefmtEval = forAllSystems (
system:
treefmt-nix.lib.evalModule nixpkgs.legacyPackages.${system} {
# Formatting configuration
programs = {
nixfmt.enable = true;
};
treefmtEval = forAllSystems (
system:
treefmt-nix.lib.evalModule nixpkgs.legacyPackages.${system} {
# Formatting configuration
programs = {
nixfmt.enable = true;
};
}
);
in
{
checks = forAllSystems (system: {
formatting = treefmtEval.${system}.config.build.check self;
});
in {
checks = forAllSystems (system: {
formatting = treefmtEval.${system}.config.build.check self;
});
formatter = forAllSystems (system: treefmtEval.${system}.config.build.wrapper);
formatter = forAllSystems (system: treefmtEval.${system}.config.build.wrapper);
packages = forAllSystems (system:
let
pkgs = nixpkgs.legacyPackages.${system};
releaseInfo = nixpkgs.lib.importJSON ./release.json;
docs = import ./docs {
inherit pkgs;
inherit (releaseInfo) release isReleaseBranch;
};
hmPkg = pkgs.callPackage ./home-manager { path = "${self}"; };
in {
default = hmPkg;
home-manager = hmPkg;
packages = forAllSystems (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
releaseInfo = nixpkgs.lib.importJSON ./release.json;
docs = import ./docs {
inherit pkgs;
inherit (releaseInfo) release isReleaseBranch;
};
hmPkg = pkgs.callPackage ./home-manager { path = "${self}"; };
in
{
default = hmPkg;
home-manager = hmPkg;
docs-html = docs.manual.html;
docs-htmlOpenTool = docs.manual.htmlOpenTool;
docs-json = docs.options.json;
docs-jsonModuleMaintainers = docs.jsonModuleMaintainers;
docs-manpages = docs.manPages;
});
});
docs-html = docs.manual.html;
docs-htmlOpenTool = docs.manual.htmlOpenTool;
docs-json = docs.options.json;
docs-jsonModuleMaintainers = docs.jsonModuleMaintainers;
docs-manpages = docs.manPages;
}
);
}
);
}

View file

@ -2,46 +2,70 @@
# file is considered internal and the exported fields may change without
# warning.
{ newsJsonFile, newsReadIdsFile ? null }:
{
newsJsonFile,
newsReadIdsFile ? null,
}:
let
inherit (builtins)
concatStringsSep filter hasAttr isString length readFile replaceStrings sort
split;
concatStringsSep
filter
hasAttr
isString
length
readFile
replaceStrings
sort
split
;
newsJson = builtins.fromJSON (builtins.readFile newsJsonFile);
# Sorted and relevant entries.
relevantEntries =
sort (a: b: a.time > b.time) (filter (e: e.condition) newsJson.entries);
relevantEntries = sort (a: b: a.time > b.time) (filter (e: e.condition) newsJson.entries);
newsReadIds = if newsReadIdsFile == null then
{ }
else
let ids = filter isString (split "\n" (readFile newsReadIdsFile));
in builtins.listToAttrs (map (id: {
name = id;
value = null;
}) ids);
newsReadIds =
if newsReadIdsFile == null then
{ }
else
let
ids = filter isString (split "\n" (readFile newsReadIdsFile));
in
builtins.listToAttrs (
map (id: {
name = id;
value = null;
}) ids
);
newsIsRead = entry: hasAttr entry.id newsReadIds;
newsUnread = let pred = entry: entry.condition && !newsIsRead entry;
in filter pred relevantEntries;
newsUnread =
let
pred = entry: entry.condition && !newsIsRead entry;
in
filter pred relevantEntries;
prettyTime = t: replaceStrings [ "T" "+00:00" ] [ " " "" ] t;
layoutNews = entries:
layoutNews =
entries:
let
mkTextEntry = entry:
let flag = if newsIsRead entry then "read" else "unread";
in ''
mkTextEntry =
entry:
let
flag = if newsIsRead entry then "read" else "unread";
in
''
* ${prettyTime entry.time} [${flag}]
${replaceStrings [ "\n" ] [ "\n " ] entry.message}
'';
in concatStringsSep "\n\n" (map mkTextEntry entries);
in {
in
concatStringsSep "\n\n" (map mkTextEntry entries);
in
{
meta = {
numUnread = length newsUnread;
display = newsJson.display;

View file

@ -1,64 +1,79 @@
{ runCommand, lib, bash, callPackage, coreutils, findutils, gettext, gnused, jq
, less, ncurses, inetutils
# used for pkgs.path for nixos-option
, pkgs
{
runCommand,
lib,
bash,
callPackage,
coreutils,
findutils,
gettext,
gnused,
jq,
less,
ncurses,
inetutils,
# used for pkgs.path for nixos-option
pkgs,
# Path to use as the Home Manager channel.
, path ? null }:
# Path to use as the Home Manager channel.
path ? null,
}:
let
pathStr = if path == null then "" else path;
nixos-option = pkgs.nixos-option or (callPackage
(pkgs.path + "/nixos/modules/installer/tools/nixos-option") { });
nixos-option =
pkgs.nixos-option or (callPackage (pkgs.path + "/nixos/modules/installer/tools/nixos-option") { });
in runCommand "home-manager" {
preferLocalBuild = true;
nativeBuildInputs = [ gettext ];
meta = {
mainProgram = "home-manager";
description = "A user environment configurator";
maintainers = [ lib.maintainers.rycee ];
platforms = lib.platforms.unix;
license = lib.licenses.mit;
};
} ''
install -v -D -m755 ${./home-manager} $out/bin/home-manager
in
runCommand "home-manager"
{
preferLocalBuild = true;
nativeBuildInputs = [ gettext ];
meta = {
mainProgram = "home-manager";
description = "A user environment configurator";
maintainers = [ lib.maintainers.rycee ];
platforms = lib.platforms.unix;
license = lib.licenses.mit;
};
}
''
install -v -D -m755 ${./home-manager} $out/bin/home-manager
substituteInPlace $out/bin/home-manager \
--subst-var-by bash "${bash}" \
--subst-var-by DEP_PATH "${
lib.makeBinPath [
coreutils
findutils
gettext
gnused
jq
less
ncurses
nixos-option
inetutils # for `hostname`
]
}" \
--subst-var-by HOME_MANAGER_LIB '${../lib/bash/home-manager.sh}' \
--subst-var-by HOME_MANAGER_PATH '${pathStr}' \
--subst-var-by OUT "$out"
substituteInPlace $out/bin/home-manager \
--subst-var-by bash "${bash}" \
--subst-var-by DEP_PATH "${
lib.makeBinPath [
coreutils
findutils
gettext
gnused
jq
less
ncurses
nixos-option
inetutils # for `hostname`
]
}" \
--subst-var-by HOME_MANAGER_LIB '${../lib/bash/home-manager.sh}' \
--subst-var-by HOME_MANAGER_PATH '${pathStr}' \
--subst-var-by OUT "$out"
install -D -m755 ${./completion.bash} \
$out/share/bash-completion/completions/home-manager
install -D -m755 ${./completion.zsh} \
$out/share/zsh/site-functions/_home-manager
install -D -m755 ${./completion.fish} \
$out/share/fish/vendor_completions.d/home-manager.fish
install -D -m755 ${./completion.bash} \
$out/share/bash-completion/completions/home-manager
install -D -m755 ${./completion.zsh} \
$out/share/zsh/site-functions/_home-manager
install -D -m755 ${./completion.fish} \
$out/share/fish/vendor_completions.d/home-manager.fish
install -D -m755 ${../lib/bash/home-manager.sh} \
"$out/share/bash/home-manager.sh"
install -D -m755 ${../lib/bash/home-manager.sh} \
"$out/share/bash/home-manager.sh"
for path in ${./po}/*.po; do
lang="''${path##*/}"
lang="''${lang%%.*}"
mkdir -p "$out/share/locale/$lang/LC_MESSAGES"
msgfmt -o "$out/share/locale/$lang/LC_MESSAGES/home-manager.mo" "$path"
done
''
for path in ${./po}/*.po; do
lang="''${path##*/}"
lang="''${lang%%.*}"
mkdir -p "$out/share/locale/$lang/LC_MESSAGES"
msgfmt -o "$out/share/locale/$lang/LC_MESSAGES/home-manager.mo" "$path"
done
''

View file

@ -1,18 +1,31 @@
{ pkgs ? import <nixpkgs> { }, confPath, confAttr ? null, check ? true
, newsReadIdsFile ? null }:
{
pkgs ? import <nixpkgs> { },
confPath,
confAttr ? null,
check ? true,
newsReadIdsFile ? null,
}:
let
inherit (pkgs.lib)
concatMapStringsSep fileContents filter length optionalString removeSuffix
replaceStrings splitString;
concatMapStringsSep
fileContents
filter
length
optionalString
removeSuffix
replaceStrings
splitString
;
env = import ../modules {
configuration = if confAttr == "" || confAttr == null then
confPath
else
(import confPath).${confAttr};
configuration =
if confAttr == "" || confAttr == null then confPath else (import confPath).${confAttr};
pkgs = pkgs;
check = check;
};
in { inherit (env) activationPackage config; }
in
{
inherit (env) activationPackage config;
}

View file

@ -8,13 +8,16 @@ let
source ${home-manager}/share/bash/home-manager.sh
'';
in runCommand "home-manager-install" {
propagatedBuildInputs = [ home-manager ];
preferLocalBuild = true;
shellHookOnly = true;
shellHook = "exec ${home-manager}/bin/home-manager init --switch --no-flake";
} ''
${hmBashLibInit}
_iError 'This derivation is not buildable, please run it using nix-shell.'
exit 1
''
in
runCommand "home-manager-install"
{
propagatedBuildInputs = [ home-manager ];
preferLocalBuild = true;
shellHookOnly = true;
shellHook = "exec ${home-manager}/bin/home-manager init --switch --no-flake";
}
''
${hmBashLibInit}
_iError 'This derivation is not buildable, please run it using nix-shell.'
exit 1
''

View file

@ -1,10 +1,21 @@
{ lib }: {
{ lib }:
{
hm = (import ../modules/lib/stdlib-extended.nix lib).hm;
homeManagerConfiguration = { modules ? [ ], pkgs, lib ? pkgs.lib
, extraSpecialArgs ? { }, check ? true
homeManagerConfiguration =
{
modules ? [ ],
pkgs,
lib ? pkgs.lib,
extraSpecialArgs ? { },
check ? true,
# Deprecated:
, configuration ? null, extraModules ? null, stateVersion ? null
, username ? null, homeDirectory ? null, system ? null }@args:
configuration ? null,
extraModules ? null,
stateVersion ? null,
username ? null,
homeDirectory ? null,
system ? null,
}@args:
let
msgForRemovedArg = ''
The 'homeManagerConfiguration' arguments
@ -20,7 +31,8 @@
'modules'. See the 22.11 release notes for more: https://nix-community.github.io/home-manager/release-notes.xhtml#sec-release-22.11-highlights
'';
throwForRemovedArgs = v:
throwForRemovedArgs =
v:
let
used = builtins.filter (n: (args.${n} or null) != null) [
"configuration"
@ -30,20 +42,34 @@
"extraModules"
"system"
];
msg = msgForRemovedArg + ''
msg =
msgForRemovedArg
+ ''
Deprecated args passed: '' + builtins.concatStringsSep " " used;
in lib.throwIf (used != [ ]) msg v;
Deprecated args passed: ''
+ builtins.concatStringsSep " " used;
in
lib.throwIf (used != [ ]) msg v;
in throwForRemovedArgs (import ../modules {
inherit pkgs lib check extraSpecialArgs;
configuration = { ... }: {
imports = modules ++ [{ programs.home-manager.path = "${../.}"; }];
nixpkgs = {
config = lib.mkDefault pkgs.config;
inherit (pkgs) overlays;
};
};
});
in
throwForRemovedArgs (
import ../modules {
inherit
pkgs
lib
check
extraSpecialArgs
;
configuration =
{ ... }:
{
imports = modules ++ [ { programs.home-manager.path = "${../.}"; } ];
nixpkgs = {
config = lib.mkDefault pkgs.config;
inherit (pkgs) overlays;
};
};
}
);
}

View file

@ -4,19 +4,22 @@ let
cfg = config.accounts.calendar;
localModule = name:
localModule =
name:
types.submodule {
options = {
path = mkOption {
type = types.str;
default = "${cfg.basePath}/${name}";
defaultText =
lib.literalExpression "accounts.calendar.basePath/name";
defaultText = lib.literalExpression "accounts.calendar.basePath/name";
description = "The path of the storage.";
};
type = mkOption {
type = types.enum [ "filesystem" "singlefile" ];
type = types.enum [
"filesystem"
"singlefile"
];
default = "filesystem";
description = "The type of the storage.";
};
@ -41,7 +44,11 @@ let
remoteModule = types.submodule {
options = {
type = mkOption {
type = types.enum [ "caldav" "http" "google_calendar" ];
type = types.enum [
"caldav"
"http"
"google_calendar"
];
description = "The type of the storage.";
};
@ -60,7 +67,10 @@ let
passwordCommand = mkOption {
type = types.nullOr (types.listOf types.str);
default = null;
example = [ "pass" "caldav" ];
example = [
"pass"
"caldav"
];
description = ''
A command that prints the password to standard output.
'';
@ -68,62 +78,66 @@ let
};
};
calendarOpts = { name, ... }: {
options = {
name = mkOption {
type = types.str;
readOnly = true;
description = ''
Unique identifier of the calendar. This is set to the
attribute name of the calendar configuration.
'';
calendarOpts =
{ name, ... }:
{
options = {
name = mkOption {
type = types.str;
readOnly = true;
description = ''
Unique identifier of the calendar. This is set to the
attribute name of the calendar configuration.
'';
};
primary = mkOption {
type = types.bool;
default = false;
description = ''
Whether this is the primary account. Only one account may be
set as primary.
'';
};
primaryCollection = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The primary collection of the account. Required when an
account has multiple collections.
'';
};
local = mkOption {
type = localModule name;
default = { };
description = ''
Local configuration for the calendar.
'';
};
remote = mkOption {
type = types.nullOr remoteModule;
default = null;
description = ''
Remote configuration for the calendar.
'';
};
};
primary = mkOption {
type = types.bool;
default = false;
description = ''
Whether this is the primary account. Only one account may be
set as primary.
'';
};
primaryCollection = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The primary collection of the account. Required when an
account has multiple collections.
'';
};
local = mkOption {
type = localModule name;
default = { };
description = ''
Local configuration for the calendar.
'';
};
remote = mkOption {
type = types.nullOr remoteModule;
default = null;
description = ''
Remote configuration for the calendar.
'';
config = {
name = name;
};
};
config = { name = name; };
};
in {
in
{
options.accounts.calendar = {
basePath = mkOption {
type = types.str;
example = ".calendar";
apply = p:
if lib.hasPrefix "/" p then p else "${config.home.homeDirectory}/${p}";
apply = p: if lib.hasPrefix "/" p then p else "${config.home.homeDirectory}/${p}";
description = ''
The base directory in which to save calendars. May be a
relative path, in which case it is relative the home
@ -132,25 +146,32 @@ in {
};
accounts = mkOption {
type = types.attrsOf (types.submodule [
calendarOpts
(import ../programs/vdirsyncer-accounts.nix)
(import ../programs/khal-accounts.nix)
(import ../programs/khal-calendar-accounts.nix)
]);
type = types.attrsOf (
types.submodule [
calendarOpts
(import ../programs/vdirsyncer-accounts.nix)
(import ../programs/khal-accounts.nix)
(import ../programs/khal-calendar-accounts.nix)
]
);
default = { };
description = "List of calendars.";
};
};
config = lib.mkIf (cfg.accounts != { }) {
assertions = let
primaries = lib.catAttrs "name"
(lib.filter (a: a.primary) (lib.attrValues cfg.accounts));
in [{
assertion = lib.length primaries <= 1;
message = "Must have at most one primary calendar account but found "
+ toString (lib.length primaries) + ", namely "
+ lib.concatStringsSep ", " primaries;
}];
assertions =
let
primaries = lib.catAttrs "name" (lib.filter (a: a.primary) (lib.attrValues cfg.accounts));
in
[
{
assertion = lib.length primaries <= 1;
message =
"Must have at most one primary calendar account but found "
+ toString (lib.length primaries)
+ ", namely "
+ lib.concatStringsSep ", " primaries;
}
];
};
}

View file

@ -5,19 +5,22 @@ let
cfg = config.accounts.contact;
localModule = name:
localModule =
name:
types.submodule {
options = {
path = mkOption {
type = types.str;
default = "${cfg.basePath}/${name}";
defaultText =
lib.literalExpression "accounts.contact.basePath/name";
defaultText = lib.literalExpression "accounts.contact.basePath/name";
description = "The path of the storage.";
};
type = mkOption {
type = types.enum [ "filesystem" "singlefile" ];
type = types.enum [
"filesystem"
"singlefile"
];
description = "The type of the storage.";
};
@ -41,7 +44,11 @@ let
remoteModule = types.submodule {
options = {
type = mkOption {
type = types.enum [ "carddav" "http" "google_contacts" ];
type = types.enum [
"carddav"
"http"
"google_contacts"
];
description = "The type of the storage.";
};
@ -69,7 +76,10 @@ let
passwordCommand = mkOption {
type = types.nullOr (types.listOf types.str);
default = null;
example = [ "pass" "caldav" ];
example = [
"pass"
"caldav"
];
description = ''
A command that prints the password to standard output.
'';
@ -77,43 +87,47 @@ let
};
};
contactOpts = { name, ... }: {
options = {
name = mkOption {
type = types.str;
readOnly = true;
description = ''
Unique identifier of the contact account. This is set to the
attribute name of the contact configuration.
'';
contactOpts =
{ name, ... }:
{
options = {
name = mkOption {
type = types.str;
readOnly = true;
description = ''
Unique identifier of the contact account. This is set to the
attribute name of the contact configuration.
'';
};
local = mkOption {
type = types.nullOr (localModule name);
default = null;
description = ''
Local configuration for the contacts.
'';
};
remote = mkOption {
type = types.nullOr remoteModule;
default = null;
description = ''
Remote configuration for the contacts.
'';
};
};
local = mkOption {
type = types.nullOr (localModule name);
default = null;
description = ''
Local configuration for the contacts.
'';
};
remote = mkOption {
type = types.nullOr remoteModule;
default = null;
description = ''
Remote configuration for the contacts.
'';
config = {
name = name;
};
};
config = { name = name; };
};
in {
in
{
options.accounts.contact = {
basePath = mkOption {
type = types.str;
apply = p:
if lib.hasPrefix "/" p then p else "${config.home.homeDirectory}/${p}";
apply = p: if lib.hasPrefix "/" p then p else "${config.home.homeDirectory}/${p}";
description = ''
The base directory in which to save contacts. May be a
relative path, in which case it is relative the home
@ -122,12 +136,14 @@ in {
};
accounts = mkOption {
type = types.attrsOf (types.submodule [
contactOpts
(import ../programs/vdirsyncer-accounts.nix)
(import ../programs/khal-accounts.nix)
(import ../programs/khal-contact-accounts.nix)
]);
type = types.attrsOf (
types.submodule [
contactOpts
(import ../programs/vdirsyncer-accounts.nix)
(import ../programs/khal-accounts.nix)
(import ../programs/khal-contact-accounts.nix)
]
);
default = { };
description = "List of contacts.";
};

View file

@ -1,7 +1,12 @@
{ config, lib, ... }:
let
inherit (lib) mkDefault mkIf mkOption types;
inherit (lib)
mkDefault
mkIf
mkOption
types
;
cfg = config.accounts.email;
@ -66,7 +71,11 @@ let
};
showSignature = mkOption {
type = types.enum [ "append" "attach" "none" ];
type = types.enum [
"append"
"attach"
"none"
];
default = "none";
description = "Method to communicate the signature.";
};
@ -195,320 +204,333 @@ let
};
};
maildirModule = types.submodule ({ config, ... }: {
options = {
path = mkOption {
type = types.str;
description = ''
Path to maildir directory where mail for this account is
stored. This is relative to the base maildir path.
'';
maildirModule = types.submodule (
{ config, ... }:
{
options = {
path = mkOption {
type = types.str;
description = ''
Path to maildir directory where mail for this account is
stored. This is relative to the base maildir path.
'';
};
absPath = mkOption {
type = types.path;
readOnly = true;
internal = true;
default = "${cfg.maildirBasePath}/${config.path}";
description = ''
A convenience option whose value is the absolute path of
this maildir.
'';
};
};
}
);
absPath = mkOption {
type = types.path;
readOnly = true;
internal = true;
default = "${cfg.maildirBasePath}/${config.path}";
description = ''
A convenience option whose value is the absolute path of
this maildir.
'';
};
};
});
mailAccountOpts =
{ name, config, ... }:
{
options = {
name = mkOption {
type = types.str;
readOnly = true;
description = ''
Unique identifier of the account. This is set to the
attribute name of the account configuration.
'';
};
mailAccountOpts = { name, config, ... }: {
options = {
name = mkOption {
type = types.str;
readOnly = true;
description = ''
Unique identifier of the account. This is set to the
attribute name of the account configuration.
'';
};
primary = mkOption {
type = types.bool;
default = false;
description = ''
Whether this is the primary account. Only one account may be
set as primary.
'';
};
primary = mkOption {
type = types.bool;
default = false;
description = ''
Whether this is the primary account. Only one account may be
set as primary.
'';
};
flavor = mkOption {
type = types.enum [
"plain"
"gmail.com"
"runbox.com"
"fastmail.com"
"yandex.com"
"outlook.office365.com"
"migadu.com"
];
default = "plain";
description = ''
Some email providers have peculiar behavior that require
special treatment. This option is therefore intended to
indicate the nature of the provider.
flavor = mkOption {
type = types.enum [
"plain"
"gmail.com"
"runbox.com"
"fastmail.com"
"yandex.com"
"outlook.office365.com"
"migadu.com"
];
default = "plain";
description = ''
Some email providers have peculiar behavior that require
special treatment. This option is therefore intended to
indicate the nature of the provider.
When this indicates a specific provider then, for example,
the IMAP, SMTP, and JMAP server configuration may be set
automatically.
'';
};
When this indicates a specific provider then, for example,
the IMAP, SMTP, and JMAP server configuration may be set
automatically.
'';
};
address = mkOption {
type = types.strMatching ".*@.*";
example = "jane.doe@example.org";
description = "The email address of this account.";
};
address = mkOption {
type = types.strMatching ".*@.*";
example = "jane.doe@example.org";
description = "The email address of this account.";
};
aliases = mkOption {
description = "Alternative identities of this account.";
default = [ ];
example = [
"webmaster@example.org"
"admin@example.org"
];
type = types.listOf (
types.oneOf [
(types.strMatching ".*@.*")
(types.submodule {
options = {
realName = mkOption {
type = types.str;
example = "Jane Doe";
description = "Name displayed when sending mails.";
};
address = mkOption {
type = types.strMatching ".*@.*";
example = "jane.doe@example.org";
description = "The email address of this identity.";
};
};
})
]
);
};
aliases = mkOption {
description = "Alternative identities of this account.";
default = [ ];
example = [ "webmaster@example.org" "admin@example.org" ];
type = types.listOf (types.oneOf [
(types.strMatching ".*@.*")
(types.submodule {
realName = mkOption {
type = types.str;
example = "Jane Doe";
description = "Name displayed when sending mails.";
};
userName = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The server username of this account. This will be used as
the SMTP, IMAP, and JMAP user name.
'';
};
passwordCommand = mkOption {
type = types.nullOr (types.either types.str (types.listOf types.str));
default = null;
apply = p: if lib.isString p then lib.splitString " " p else p;
example = "secret-tool lookup email me@example.org";
description = ''
A command, which when run writes the account password on
standard output.
'';
};
folders = mkOption {
type = types.submodule {
options = {
realName = mkOption {
inbox = mkOption {
type = types.str;
example = "Jane Doe";
description = "Name displayed when sending mails.";
default = "Inbox";
description = ''
Relative path of the inbox mail.
'';
};
address = mkOption {
type = types.strMatching ".*@.*";
example = "jane.doe@example.org";
description = "The email address of this identity.";
sent = mkOption {
type = types.nullOr types.str;
default = "Sent";
description = ''
Relative path of the sent mail folder.
'';
};
};
})
]);
};
realName = mkOption {
type = types.str;
example = "Jane Doe";
description = "Name displayed when sending mails.";
};
drafts = mkOption {
type = types.nullOr types.str;
default = "Drafts";
description = ''
Relative path of the drafts mail folder.
'';
};
userName = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The server username of this account. This will be used as
the SMTP, IMAP, and JMAP user name.
'';
};
passwordCommand = mkOption {
type = types.nullOr (types.either types.str (types.listOf types.str));
default = null;
apply = p: if lib.isString p then lib.splitString " " p else p;
example = "secret-tool lookup email me@example.org";
description = ''
A command, which when run writes the account password on
standard output.
'';
};
folders = mkOption {
type = types.submodule {
options = {
inbox = mkOption {
type = types.str;
default = "Inbox";
description = ''
Relative path of the inbox mail.
'';
};
sent = mkOption {
type = types.nullOr types.str;
default = "Sent";
description = ''
Relative path of the sent mail folder.
'';
};
drafts = mkOption {
type = types.nullOr types.str;
default = "Drafts";
description = ''
Relative path of the drafts mail folder.
'';
};
trash = mkOption {
type = types.str;
default = "Trash";
description = ''
Relative path of the deleted mail folder.
'';
trash = mkOption {
type = types.str;
default = "Trash";
description = ''
Relative path of the deleted mail folder.
'';
};
};
};
default = { };
description = ''
Standard email folders.
'';
};
imap = mkOption {
type = types.nullOr imapModule;
default = null;
description = ''
The IMAP configuration to use for this account.
'';
};
jmap = mkOption {
type = types.nullOr jmapModule;
default = null;
description = ''
The JMAP configuration to use for this account.
'';
};
signature = mkOption {
type = signatureModule;
default = { };
description = ''
Signature configuration.
'';
};
gpg = mkOption {
type = types.nullOr gpgModule;
default = null;
description = ''
GPG configuration.
'';
};
smtp = mkOption {
type = types.nullOr smtpModule;
default = null;
description = ''
The SMTP configuration to use for this account.
'';
};
maildir = mkOption {
type = types.nullOr maildirModule;
defaultText = {
path = "\${name}";
};
description = ''
Maildir configuration for this account.
'';
};
default = { };
description = ''
Standard email folders.
'';
};
imap = mkOption {
type = types.nullOr imapModule;
default = null;
description = ''
The IMAP configuration to use for this account.
'';
};
config = lib.mkMerge [
{
name = name;
maildir = lib.mkOptionDefault { path = "${name}"; };
}
jmap = mkOption {
type = types.nullOr jmapModule;
default = null;
description = ''
The JMAP configuration to use for this account.
'';
};
(mkIf (config.flavor == "yandex.com") {
userName = mkDefault config.address;
signature = mkOption {
type = signatureModule;
default = { };
description = ''
Signature configuration.
'';
};
imap = {
host = "imap.yandex.com";
port = 993;
tls.enable = true;
};
gpg = mkOption {
type = types.nullOr gpgModule;
default = null;
description = ''
GPG configuration.
'';
};
smtp = {
host = "smtp.yandex.com";
port = 465;
tls.enable = true;
};
})
smtp = mkOption {
type = types.nullOr smtpModule;
default = null;
description = ''
The SMTP configuration to use for this account.
'';
};
(mkIf (config.flavor == "outlook.office365.com") {
userName = mkDefault config.address;
maildir = mkOption {
type = types.nullOr maildirModule;
defaultText = { path = "\${name}"; };
description = ''
Maildir configuration for this account.
'';
};
imap = {
host = "outlook.office365.com";
port = 993;
tls.enable = true;
};
smtp = {
host = "smtp.office365.com";
port = 587;
tls = {
enable = true;
useStartTls = true;
};
};
})
(mkIf (config.flavor == "fastmail.com") {
userName = mkDefault config.address;
imap = {
host = "imap.fastmail.com";
port = 993;
};
smtp = {
host = "smtp.fastmail.com";
port = if config.smtp.tls.useStartTls then 587 else 465;
};
jmap = {
host = "fastmail.com";
sessionUrl = "https://jmap.fastmail.com/.well-known/jmap";
};
})
(mkIf (config.flavor == "migadu.com") {
userName = mkDefault config.address;
imap = {
host = "imap.migadu.com";
port = 993;
};
smtp = {
host = "smtp.migadu.com";
port = 465;
};
})
(mkIf (config.flavor == "gmail.com") {
userName = mkDefault config.address;
imap = {
host = "imap.gmail.com";
port = 993;
};
smtp = {
host = "smtp.gmail.com";
port = if config.smtp.tls.useStartTls then 587 else 465;
};
})
(mkIf (config.flavor == "runbox.com") {
imap = {
host = "mail.runbox.com";
port = 993;
};
smtp = {
host = "mail.runbox.com";
port = if config.smtp.tls.useStartTls then 587 else 465;
};
})
];
};
config = lib.mkMerge [
{
name = name;
maildir = lib.mkOptionDefault { path = "${name}"; };
}
(mkIf (config.flavor == "yandex.com") {
userName = mkDefault config.address;
imap = {
host = "imap.yandex.com";
port = 993;
tls.enable = true;
};
smtp = {
host = "smtp.yandex.com";
port = 465;
tls.enable = true;
};
})
(mkIf (config.flavor == "outlook.office365.com") {
userName = mkDefault config.address;
imap = {
host = "outlook.office365.com";
port = 993;
tls.enable = true;
};
smtp = {
host = "smtp.office365.com";
port = 587;
tls = {
enable = true;
useStartTls = true;
};
};
})
(mkIf (config.flavor == "fastmail.com") {
userName = mkDefault config.address;
imap = {
host = "imap.fastmail.com";
port = 993;
};
smtp = {
host = "smtp.fastmail.com";
port = if config.smtp.tls.useStartTls then 587 else 465;
};
jmap = {
host = "fastmail.com";
sessionUrl = "https://jmap.fastmail.com/.well-known/jmap";
};
})
(mkIf (config.flavor == "migadu.com") {
userName = mkDefault config.address;
imap = {
host = "imap.migadu.com";
port = 993;
};
smtp = {
host = "smtp.migadu.com";
port = 465;
};
})
(mkIf (config.flavor == "gmail.com") {
userName = mkDefault config.address;
imap = {
host = "imap.gmail.com";
port = 993;
};
smtp = {
host = "smtp.gmail.com";
port = if config.smtp.tls.useStartTls then 587 else 465;
};
})
(mkIf (config.flavor == "runbox.com") {
imap = {
host = "mail.runbox.com";
port = 993;
};
smtp = {
host = "mail.runbox.com";
port = if config.smtp.tls.useStartTls then 587 else 465;
};
})
];
};
in {
in
{
options.accounts.email = {
certificatesFile = mkOption {
type = types.nullOr types.path;
@ -524,8 +546,7 @@ in {
type = types.str;
default = "${config.home.homeDirectory}/Maildir";
defaultText = "Maildir";
apply = p:
if lib.hasPrefix "/" p then p else "${config.home.homeDirectory}/${p}";
apply = p: if lib.hasPrefix "/" p then p else "${config.home.homeDirectory}/${p}";
description = ''
The base directory for account maildir directories. May be a
relative path (e.g. the user setting this value as "MyMaildir"),
@ -543,16 +564,18 @@ in {
config = mkIf (cfg.accounts != { }) {
assertions = [
(let
primaries = lib.catAttrs "name"
(lib.filter (a: a.primary) (lib.attrValues cfg.accounts));
in {
assertion = lib.length primaries == 1;
message = "Must have exactly one primary mail account but found "
+ toString (lib.length primaries)
+ lib.optionalString (lib.length primaries > 1)
(", namely " + lib.concatStringsSep ", " primaries);
})
(
let
primaries = lib.catAttrs "name" (lib.filter (a: a.primary) (lib.attrValues cfg.accounts));
in
{
assertion = lib.length primaries == 1;
message =
"Must have exactly one primary mail account but found "
+ toString (lib.length primaries)
+ lib.optionalString (lib.length primaries > 1) (", namely " + lib.concatStringsSep ", " primaries);
}
)
];
};
}

View file

@ -1,9 +1,27 @@
{ config, options, lib, pkgs, ... }:
{
config,
options,
lib,
pkgs,
...
}:
let
inherit (lib)
mkEnableOption mkOption mkIf mkMerge mkDefault mkAliasOptionModule types
literalExpression escapeShellArg hm getAttrFromPath any optional;
mkEnableOption
mkOption
mkIf
mkMerge
mkDefault
mkAliasOptionModule
types
literalExpression
escapeShellArg
hm
getAttrFromPath
any
optional
;
cfg = config.home.pointerCursor;
opts = options.home.pointerCursor;
@ -51,11 +69,13 @@ let
};
dotIcons = {
enable = mkEnableOption ''
`.icons` config generation for {option}`home.pointerCursor`
'' // {
default = true;
};
enable =
mkEnableOption ''
`.icons` config generation for {option}`home.pointerCursor`
''
// {
default = true;
};
};
hyprcursor = {
@ -70,15 +90,12 @@ let
};
sway = {
enable = mkEnableOption
"sway config generation for {option}`home.pointerCursor`";
enable = mkEnableOption "sway config generation for {option}`home.pointerCursor`";
};
};
};
cursorPath = "${cfg.package}/share/icons/${escapeShellArg cfg.name}/cursors/${
escapeShellArg cfg.x11.defaultCursor
}";
cursorPath = "${cfg.package}/share/icons/${escapeShellArg cfg.name}/cursors/${escapeShellArg cfg.x11.defaultCursor}";
defaultIndexThemePackage = pkgs.writeTextFile {
name = "index.theme";
@ -94,31 +111,44 @@ let
'';
};
in {
in
{
meta.maintainers = [ lib.maintainers.league ];
imports = [
(mkAliasOptionModule [ "xsession" "pointerCursor" "package" ] [
"home"
"pointerCursor"
"package"
])
(mkAliasOptionModule [ "xsession" "pointerCursor" "name" ] [
"home"
"pointerCursor"
"name"
])
(mkAliasOptionModule [ "xsession" "pointerCursor" "size" ] [
"home"
"pointerCursor"
"size"
])
(mkAliasOptionModule [ "xsession" "pointerCursor" "defaultCursor" ] [
"home"
"pointerCursor"
"x11"
"defaultCursor"
])
(mkAliasOptionModule
[ "xsession" "pointerCursor" "package" ]
[
"home"
"pointerCursor"
"package"
]
)
(mkAliasOptionModule
[ "xsession" "pointerCursor" "name" ]
[
"home"
"pointerCursor"
"name"
]
)
(mkAliasOptionModule
[ "xsession" "pointerCursor" "size" ]
[
"home"
"pointerCursor"
"size"
]
)
(mkAliasOptionModule
[ "xsession" "pointerCursor" "defaultCursor" ]
[
"home"
"pointerCursor"
"x11"
"defaultCursor"
]
)
];
options = {
@ -145,107 +175,121 @@ in {
};
};
config = let
# Check if enable option was explicitly defined by the user
enableDefined = any (x: x ? enable) opts.definitions;
config =
let
# Check if enable option was explicitly defined by the user
enableDefined = any (x: x ? enable) opts.definitions;
# Determine if cursor configuration should be enabled
enable = if enableDefined then cfg.enable else cfg != null;
in mkMerge [
(mkIf enable (mkMerge [
{
assertions = [
(hm.assertions.assertPlatform "home.pointerCursor" pkgs
lib.platforms.linux)
];
# Determine if cursor configuration should be enabled
enable = if enableDefined then cfg.enable else cfg != null;
in
mkMerge [
(mkIf enable (mkMerge [
{
assertions = [
(hm.assertions.assertPlatform "home.pointerCursor" pkgs lib.platforms.linux)
];
home.packages = [ cfg.package defaultIndexThemePackage ];
home.packages = [
cfg.package
defaultIndexThemePackage
];
home.sessionVariables = {
XCURSOR_SIZE = mkDefault cfg.size;
XCURSOR_THEME = mkDefault cfg.name;
};
home.sessionVariables = {
XCURSOR_SIZE = mkDefault cfg.size;
XCURSOR_THEME = mkDefault cfg.name;
};
# Set directory to look for cursors in, needed for some applications
# that are unable to find cursors otherwise. See:
# https://github.com/nix-community/home-manager/issues/2812
# https://wiki.archlinux.org/title/Cursor_themes#Environment_variable
home.sessionSearchVariables.XCURSOR_PATH =
[ "${config.home.profileDirectory}/share/icons" ];
# Set directory to look for cursors in, needed for some applications
# that are unable to find cursors otherwise. See:
# https://github.com/nix-community/home-manager/issues/2812
# https://wiki.archlinux.org/title/Cursor_themes#Environment_variable
home.sessionSearchVariables.XCURSOR_PATH = [ "${config.home.profileDirectory}/share/icons" ];
# Add cursor icon link to $XDG_DATA_HOME/icons as well for redundancy.
xdg.dataFile."icons/default/index.theme".source =
"${defaultIndexThemePackage}/share/icons/default/index.theme";
xdg.dataFile."icons/${cfg.name}".source =
"${cfg.package}/share/icons/${cfg.name}";
}
# Add cursor icon link to $XDG_DATA_HOME/icons as well for redundancy.
xdg.dataFile."icons/default/index.theme".source =
"${defaultIndexThemePackage}/share/icons/default/index.theme";
xdg.dataFile."icons/${cfg.name}".source = "${cfg.package}/share/icons/${cfg.name}";
}
(mkIf cfg.dotIcons.enable {
# Add symlink of cursor icon directory to $HOME/.icons, needed for
# backwards compatibility with some applications. See:
# https://specifications.freedesktop.org/icon-theme-spec/latest/ar01s03.html
home.file.".icons/default/index.theme".source =
"${defaultIndexThemePackage}/share/icons/default/index.theme";
home.file.".icons/${cfg.name}".source =
"${cfg.package}/share/icons/${cfg.name}";
})
(mkIf cfg.dotIcons.enable {
# Add symlink of cursor icon directory to $HOME/.icons, needed for
# backwards compatibility with some applications. See:
# https://specifications.freedesktop.org/icon-theme-spec/latest/ar01s03.html
home.file.".icons/default/index.theme".source =
"${defaultIndexThemePackage}/share/icons/default/index.theme";
home.file.".icons/${cfg.name}".source = "${cfg.package}/share/icons/${cfg.name}";
})
(mkIf cfg.x11.enable {
xsession.profileExtra = ''
${pkgs.xorg.xsetroot}/bin/xsetroot -xcf ${cursorPath} ${
toString cfg.size
}
'';
(mkIf cfg.x11.enable {
xsession.profileExtra = ''
${pkgs.xorg.xsetroot}/bin/xsetroot -xcf ${cursorPath} ${toString cfg.size}
'';
xresources.properties = {
"Xcursor.theme" = cfg.name;
"Xcursor.size" = cfg.size;
};
})
xresources.properties = {
"Xcursor.theme" = cfg.name;
"Xcursor.size" = cfg.size;
};
})
(mkIf cfg.gtk.enable {
gtk.cursorTheme = mkDefault { inherit (cfg) package name size; };
})
(mkIf cfg.gtk.enable {
gtk.cursorTheme = mkDefault { inherit (cfg) package name size; };
})
(mkIf cfg.hyprcursor.enable {
home.sessionVariables = {
HYPRCURSOR_THEME = cfg.name;
HYPRCURSOR_SIZE = if cfg.hyprcursor.size != null then
cfg.hyprcursor.size
else
cfg.size;
};
})
(mkIf cfg.hyprcursor.enable {
home.sessionVariables = {
HYPRCURSOR_THEME = cfg.name;
HYPRCURSOR_SIZE = if cfg.hyprcursor.size != null then cfg.hyprcursor.size else cfg.size;
};
})
(mkIf cfg.sway.enable {
wayland.windowManager.sway = {
config = {
seat = {
"*" = {
xcursor_theme =
"${cfg.name} ${toString config.gtk.cursorTheme.size}";
(mkIf cfg.sway.enable {
wayland.windowManager.sway = {
config = {
seat = {
"*" = {
xcursor_theme = "${cfg.name} ${toString config.gtk.cursorTheme.size}";
};
};
};
};
};
})
]))
})
]))
{
warnings = (optional (any (x:
getAttrFromPath
([ "xsession" "pointerCursor" ] ++ [ x ] ++ [ "isDefined" ])
options) [ "package" "name" "size" "defaultCursor" ]) ''
The option `xsession.pointerCursor` has been merged into `home.pointerCursor` and will be removed
in the future. Please change to set `home.pointerCursor` directly and enable `home.pointerCursor.x11.enable`
to generate x11 specific cursor configurations. You can refer to the documentation for more details.
'') ++ (optional (opts.highestPrio != (lib.mkOptionDefault { }).priority
&& cfg == null) ''
{
warnings =
(optional
(any
(
x:
getAttrFromPath (
[
"xsession"
"pointerCursor"
]
++ [ x ]
++ [ "isDefined" ]
) options
)
[
"package"
"name"
"size"
"defaultCursor"
]
)
''
The option `xsession.pointerCursor` has been merged into `home.pointerCursor` and will be removed
in the future. Please change to set `home.pointerCursor` directly and enable `home.pointerCursor.x11.enable`
to generate x11 specific cursor configurations. You can refer to the documentation for more details.
''
)
++ (optional (opts.highestPrio != (lib.mkOptionDefault { }).priority && cfg == null) ''
Setting home.pointerCursor to null is deprecated.
Please update your configuration to explicitly set:
home.pointerCursor.enable = false;
'');
}
];
}
];
}

View file

@ -15,7 +15,12 @@
# below for changes:
# https://github.com/NixOS/nixpkgs/blob/nixpkgs-unstable/pkgs/development/libraries/glibc/nix-locale-archive.patch
{ lib, pkgs, config, ... }:
{
lib,
pkgs,
config,
...
}:
let
inherit (config.i18n) glibcLocales;
@ -25,14 +30,20 @@ let
archivePath = "${glibcLocales}/lib/locale/locale-archive";
# lookup the version of glibcLocales and set the appropriate environment vars
localeVars = if lib.versionAtLeast version "2.27" then {
LOCALE_ARCHIVE_2_27 = archivePath;
} else if lib.versionAtLeast version "2.11" then {
LOCALE_ARCHIVE_2_11 = archivePath;
} else
{ };
localeVars =
if lib.versionAtLeast version "2.27" then
{
LOCALE_ARCHIVE_2_27 = archivePath;
}
else if lib.versionAtLeast version "2.11" then
{
LOCALE_ARCHIVE_2_11 = archivePath;
}
else
{ };
in {
in
{
meta.maintainers = with lib.maintainers; [ midchildan ];
options = {

View file

@ -1,18 +1,24 @@
{ configuration, pkgs, lib ? pkgs.lib
{
configuration,
pkgs,
lib ? pkgs.lib,
# Whether to check that each option has a matching declaration.
, check ? true
check ? true,
# Extra arguments passed to specialArgs.
, extraSpecialArgs ? { } }:
extraSpecialArgs ? { },
}:
let
collectFailed = cfg:
map (x: x.message) (lib.filter (x: !x.assertion) cfg.assertions);
collectFailed = cfg: map (x: x.message) (lib.filter (x: !x.assertion) cfg.assertions);
showWarnings = res:
let f = w: x: builtins.trace "warning: ${w}" x;
in lib.fold f res res.config.warnings;
showWarnings =
res:
let
f = w: x: builtins.trace "warning: ${w}" x;
in
lib.fold f res res.config.warnings;
extendedLib = import ./lib/stdlib-extended.nix lib;
@ -24,24 +30,33 @@ let
rawModule = extendedLib.evalModules {
modules = [ configuration ] ++ hmModules;
class = "homeManager";
specialArgs = { modulesPath = builtins.toString ./.; } // extraSpecialArgs;
specialArgs = {
modulesPath = builtins.toString ./.;
} // extraSpecialArgs;
};
moduleChecks = raw:
showWarnings (let
failed = collectFailed raw.config;
failedStr = lib.concatStringsSep "\n" (map (x: "- ${x}") failed);
in if failed == [ ] then
raw
else
throw ''
moduleChecks =
raw:
showWarnings (
let
failed = collectFailed raw.config;
failedStr = lib.concatStringsSep "\n" (map (x: "- ${x}") failed);
in
if failed == [ ] then
raw
else
throw ''
Failed assertions:
${failedStr}'');
Failed assertions:
${failedStr}''
);
withExtraAttrs = rawModule:
let module = moduleChecks rawModule;
in {
withExtraAttrs =
rawModule:
let
module = moduleChecks rawModule;
in
{
inherit (module) options config;
activationPackage = module.config.home.activationPackage;
@ -50,11 +65,13 @@ let
activation-script = module.config.home.activationPackage;
newsDisplay = rawModule.config.news.display;
newsEntries = lib.sort (a: b: a.time > b.time)
(lib.filter (a: a.condition) rawModule.config.news.entries);
newsEntries = lib.sort (a: b: a.time > b.time) (
lib.filter (a: a.condition) rawModule.config.news.entries
);
inherit (module._module.args) pkgs;
extendModules = args: withExtraAttrs (rawModule.extendModules args);
};
in withExtraAttrs rawModule
in
withExtraAttrs rawModule

View file

@ -1,4 +1,9 @@
{ pkgs, config, lib, ... }:
{
pkgs,
config,
lib,
...
}:
let
@ -6,18 +11,24 @@ let
homeDirectory = config.home.homeDirectory;
fileType = (import lib/file-type.nix {
inherit homeDirectory lib pkgs;
}).fileType;
fileType =
(import lib/file-type.nix {
inherit homeDirectory lib pkgs;
}).fileType;
sourceStorePath = file:
sourceStorePath =
file:
let
sourcePath = toString file.source;
sourceName = config.lib.strings.storeFileName (baseNameOf sourcePath);
in
if builtins.hasContext sourcePath
then file.source
else builtins.path { path = file.source; name = sourceName; };
if builtins.hasContext sourcePath then
file.source
else
builtins.path {
path = file.source;
name = sourceName;
};
in
@ -25,7 +36,7 @@ in
options = {
home.file = lib.mkOption {
description = "Attribute set of files to link into the user home.";
default = {};
default = { };
type = fileType "home.file" "{env}`HOME`" homeDirectory;
};
@ -37,26 +48,29 @@ in
};
config = {
assertions = [(
let
dups =
lib.attrNames
(lib.filterAttrs (n: v: v > 1)
(lib.foldAttrs (acc: v: acc + v) 0
(lib.mapAttrsToList (n: v: { ${v.target} = 1; }) cfg)));
dupsStr = lib.concatStringsSep ", " dups;
in {
assertion = dups == [];
message = ''
Conflicting managed target files: ${dupsStr}
assertions = [
(
let
dups = lib.attrNames (
lib.filterAttrs (n: v: v > 1) (
lib.foldAttrs (acc: v: acc + v) 0 (lib.mapAttrsToList (n: v: { ${v.target} = 1; }) cfg)
)
);
dupsStr = lib.concatStringsSep ", " dups;
in
{
assertion = dups == [ ];
message = ''
Conflicting managed target files: ${dupsStr}
This may happen, for example, if you have a configuration similar to
This may happen, for example, if you have a configuration similar to
home.file = {
conflict1 = { source = ./foo.nix; target = "baz"; };
conflict2 = { source = ./bar.nix; target = "baz"; };
}'';
})
home.file = {
conflict1 = { source = ./foo.nix; target = "baz"; };
conflict2 = { source = ./bar.nix; target = "baz"; };
}'';
}
)
];
# Using this function it is possible to make `home.file` create a
@ -67,23 +81,23 @@ in
#
# would upon activation create a symlink `~/foo` that points to the
# absolute path of the `bar` file relative the configuration file.
lib.file.mkOutOfStoreSymlink = path:
lib.file.mkOutOfStoreSymlink =
path:
let
pathStr = toString path;
name = lib.hm.strings.storeFileName (baseNameOf pathStr);
in
pkgs.runCommandLocal name {} ''ln -s ${lib.escapeShellArg pathStr} $out'';
pkgs.runCommandLocal name { } ''ln -s ${lib.escapeShellArg pathStr} $out'';
# This verifies that the links we are about to create will not
# overwrite an existing file.
home.activation.checkLinkTargets = lib.hm.dag.entryBefore ["writeBoundary"] (
home.activation.checkLinkTargets = lib.hm.dag.entryBefore [ "writeBoundary" ] (
let
# Paths that should be forcibly overwritten by Home Manager.
# Caveat emptor!
forcedPaths =
lib.concatMapStringsSep " " (p: ''"$HOME"/${lib.escapeShellArg p}'')
(lib.mapAttrsToList (n: v: v.target)
(lib.filterAttrs (n: v: v.force) cfg));
forcedPaths = lib.concatMapStringsSep " " (p: ''"$HOME"/${lib.escapeShellArg p}'') (
lib.mapAttrsToList (n: v: v.target) (lib.filterAttrs (n: v: v.force) cfg)
);
storeDir = lib.escapeShellArg builtins.storeDir;
@ -124,7 +138,7 @@ in
# 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 = lib.hm.dag.entryAfter ["writeBoundary"] (
home.activation.linkGeneration = lib.hm.dag.entryAfter [ "writeBoundary" ] (
let
link = pkgs.writeShellScript "link" ''
${config.lib.bash.initHomeManagerLib}
@ -189,43 +203,44 @@ in
done
'';
in
''
function linkNewGen() {
_i "Creating home file links in %s" "$HOME"
''
function linkNewGen() {
_i "Creating home file links in %s" "$HOME"
local newGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
find "$newGenFiles" \( -type f -or -type l \) \
-exec bash ${link} "$newGenFiles" {} +
}
local newGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
find "$newGenFiles" \( -type f -or -type l \) \
-exec bash ${link} "$newGenFiles" {} +
}
function cleanOldGen() {
if [[ ! -v oldGenPath || ! -e "$oldGenPath/home-files" ]] ; then
return
fi
function cleanOldGen() {
if [[ ! -v oldGenPath || ! -e "$oldGenPath/home-files" ]] ; then
return
fi
_i "Cleaning up orphan links from %s" "$HOME"
_i "Cleaning up orphan links from %s" "$HOME"
local newGenFiles oldGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
oldGenFiles="$(readlink -e "$oldGenPath/home-files")"
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"
}
# 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
linkNewGen
''
cleanOldGen
linkNewGen
''
);
home.activation.checkFilesChanged = lib.hm.dag.entryBefore ["linkGeneration"] (
home.activation.checkFilesChanged = lib.hm.dag.entryBefore [ "linkGeneration" ] (
let
homeDirArg = lib.escapeShellArg homeDirectory;
in ''
in
''
function _cmp() {
if [[ -d $1 && -d $2 ]]; then
diff -rq "$1" "$2" &> /dev/null
@ -234,21 +249,25 @@ in
fi
}
declare -A changedFiles
'' + lib.concatMapStrings (v:
''
+ lib.concatMapStrings (
v:
let
sourceArg = lib.escapeShellArg (sourceStorePath v);
targetArg = lib.escapeShellArg v.target;
in ''
in
''
_cmp ${sourceArg} ${homeDirArg}/${targetArg} \
&& changedFiles[${targetArg}]=0 \
|| changedFiles[${targetArg}]=1
'') (lib.filter (v: v.onChange != "") (lib.attrValues cfg))
''
) (lib.filter (v: v.onChange != "") (lib.attrValues cfg))
+ ''
unset -f _cmp
''
);
home.activation.onFilesChange = lib.hm.dag.entryAfter ["linkGeneration"] (
home.activation.onFilesChange = lib.hm.dag.entryAfter [ "linkGeneration" ] (
lib.concatMapStrings (v: ''
if (( ''${changedFiles[${lib.escapeShellArg v.target}]} == 1 )); then
if [[ -v DRY_RUN || -v VERBOSE ]]; then
@ -263,85 +282,87 @@ in
# Symlink directories and files that have the right execute bit.
# Copy files that need their execute bit changed.
home-files = pkgs.runCommandLocal
"home-manager-files"
{
nativeBuildInputs = [ pkgs.xorg.lndir ];
}
(''
mkdir -p $out
# Needed in case /nix is a symbolic link.
realOut="$(realpath -m "$out")"
function insertFile() {
local source="$1"
local relTarget="$2"
local executable="$3"
local recursive="$4"
# If the target already exists then we have a collision. Note, this
# should not happen due to the assertion found in the 'files' module.
# We therefore simply log the conflict and otherwise ignore it, mainly
# to make the `files-target-config` test work as expected.
if [[ -e "$realOut/$relTarget" ]]; then
echo "File conflict for file '$relTarget'" >&2
return
fi
# Figure out the real absolute path to the target.
local target
target="$(realpath -m "$realOut/$relTarget")"
# Target path must be within $HOME.
if [[ ! $target == $realOut* ]] ; 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
home-files =
pkgs.runCommandLocal "home-manager-files"
{
nativeBuildInputs = [ pkgs.xorg.lndir ];
}
'' + lib.concatStrings (
lib.mapAttrsToList (n: v: ''
insertFile ${
lib.escapeShellArgs [
(sourceStorePath v)
v.target
(if v.executable == null
then "inherit"
else toString v.executable)
(toString v.recursive)
]}
'') cfg
));
(
''
mkdir -p $out
# Needed in case /nix is a symbolic link.
realOut="$(realpath -m "$out")"
function insertFile() {
local source="$1"
local relTarget="$2"
local executable="$3"
local recursive="$4"
# If the target already exists then we have a collision. Note, this
# should not happen due to the assertion found in the 'files' module.
# We therefore simply log the conflict and otherwise ignore it, mainly
# to make the `files-target-config` test work as expected.
if [[ -e "$realOut/$relTarget" ]]; then
echo "File conflict for file '$relTarget'" >&2
return
fi
# Figure out the real absolute path to the target.
local target
target="$(realpath -m "$realOut/$relTarget")"
# Target path must be within $HOME.
if [[ ! $target == $realOut* ]] ; 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
}
''
+ lib.concatStrings (
lib.mapAttrsToList (n: v: ''
insertFile ${
lib.escapeShellArgs [
(sourceStorePath v)
v.target
(if v.executable == null then "inherit" else toString v.executable)
(toString v.recursive)
]
}
'') cfg
)
);
};
}

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) literalExpression mkOption types;
@ -112,10 +117,7 @@ let
options = {
layout = mkOption {
type = with types; nullOr str;
default =
if lib.versionAtLeast config.home.stateVersion "19.09"
then null
else "us";
default = if lib.versionAtLeast config.home.stateVersion "19.09" then null else "us";
defaultText = literalExpression "null";
description = ''
Keyboard layout. If `null`, then the system
@ -137,8 +139,11 @@ let
options = mkOption {
type = types.listOf types.str;
default = [];
example = ["grp:caps_toggle" "grp_led:scroll"];
default = [ ];
example = [
"grp:caps_toggle"
"grp_led:scroll"
];
description = ''
X keyboard options; layout switching goes here.
'';
@ -146,10 +151,7 @@ let
variant = mkOption {
type = with types; nullOr str;
default =
if lib.versionAtLeast config.home.stateVersion "19.09"
then null
else "";
default = if lib.versionAtLeast config.home.stateVersion "19.09" then null else "";
defaultText = literalExpression "null";
example = "colemak";
description = ''
@ -216,7 +218,7 @@ in
home.language = mkOption {
type = languageSubModule;
default = {};
default = { };
description = "Language configuration.";
};
@ -254,9 +256,19 @@ in
};
home.sessionVariables = mkOption {
default = {};
type = with types; lazyAttrsOf (oneOf [ str path int float ]);
example = { EDITOR = "emacs"; GS_OPTIONS = "-sPAPERSIZE=a4"; };
default = { };
type =
with types;
lazyAttrsOf (oneOf [
str
path
int
float
]);
example = {
EDITOR = "emacs";
GS_OPTIONS = "-sPAPERSIZE=a4";
};
description = ''
Environment variables to always set at login.
@ -352,14 +364,18 @@ in
home.packages = mkOption {
type = types.listOf types.package;
default = [];
default = [ ];
description = "The set of packages to appear in the user environment.";
};
home.extraOutputsToInstall = mkOption {
type = types.listOf types.str;
default = [];
example = [ "doc" "info" "devdoc" ];
default = [ ];
example = [
"doc"
"info"
"devdoc"
];
description = ''
List of additional package outputs of the packages
{var}`home.packages` that should be installed into
@ -391,7 +407,7 @@ in
home.activation = mkOption {
type = lib.hm.types.dagOf types.str;
default = {};
default = { };
example = literalExpression ''
{
myActivationAction = lib.hm.dag.entryAfter ["writeBoundary"] '''
@ -518,41 +534,39 @@ in
let
hmRelease = config.home.version.release;
nixpkgsRelease = lib.trivial.release;
releaseMismatch =
config.home.enableNixpkgsReleaseCheck
&& hmRelease != nixpkgsRelease;
releaseMismatch = config.home.enableNixpkgsReleaseCheck && hmRelease != nixpkgsRelease;
in
lib.optional releaseMismatch ''
You are using
lib.optional releaseMismatch ''
You are using
Home Manager version ${hmRelease} and
Nixpkgs version ${nixpkgsRelease}.
Home Manager version ${hmRelease} and
Nixpkgs version ${nixpkgsRelease}.
Using mismatched versions is likely to cause errors and unexpected
behavior. It is therefore highly recommended to use a release of Home
Manager that corresponds with your chosen release of Nixpkgs.
Using mismatched versions is likely to cause errors and unexpected
behavior. It is therefore highly recommended to use a release of Home
Manager that corresponds with your chosen release of Nixpkgs.
If you insist then you can disable this warning by adding
If you insist then you can disable this warning by adding
home.enableNixpkgsReleaseCheck = false;
home.enableNixpkgsReleaseCheck = false;
to your configuration.
'';
to your configuration.
'';
home.username =
lib.mkIf (lib.versionOlder config.home.stateVersion "20.09")
(lib.mkDefault (builtins.getEnv "USER"));
home.homeDirectory =
lib.mkIf (lib.versionOlder config.home.stateVersion "20.09")
(lib.mkDefault (builtins.getEnv "HOME"));
home.username = lib.mkIf (lib.versionOlder config.home.stateVersion "20.09") (
lib.mkDefault (builtins.getEnv "USER")
);
home.homeDirectory = lib.mkIf (lib.versionOlder config.home.stateVersion "20.09") (
lib.mkDefault (builtins.getEnv "HOME")
);
home.profileDirectory =
if config.submoduleSupport.enable
&& config.submoduleSupport.externalPackageInstall
then "/etc/profiles/per-user/${cfg.username}"
else if config.nix.enable && (config.nix.settings.use-xdg-base-directories or false)
then "${config.xdg.stateHome}/nix/profile"
else cfg.homeDirectory + "/.nix-profile";
if config.submoduleSupport.enable && config.submoduleSupport.externalPackageInstall then
"/etc/profiles/per-user/${cfg.username}"
else if config.nix.enable && (config.nix.settings.use-xdg-base-directories or false) then
"${config.xdg.stateHome}/nix/profile"
else
cfg.homeDirectory + "/.nix-profile";
programs.bash.shellAliases = cfg.shellAliases;
programs.zsh.shellAliases = cfg.shellAliases;
@ -563,51 +577,41 @@ in
let
maybeSet = n: v: lib.optionalAttrs (v != null) { ${n} = v; };
in
(maybeSet "LANG" cfg.language.base)
//
(maybeSet "LC_CTYPE" cfg.language.ctype)
//
(maybeSet "LC_NUMERIC" cfg.language.numeric)
//
(maybeSet "LC_TIME" cfg.language.time)
//
(maybeSet "LC_COLLATE" cfg.language.collate)
//
(maybeSet "LC_MONETARY" cfg.language.monetary)
//
(maybeSet "LC_MESSAGES" cfg.language.messages)
//
(maybeSet "LC_PAPER" cfg.language.paper)
//
(maybeSet "LC_NAME" cfg.language.name)
//
(maybeSet "LC_ADDRESS" cfg.language.address)
//
(maybeSet "LC_TELEPHONE" cfg.language.telephone)
//
(maybeSet "LC_MEASUREMENT" cfg.language.measurement);
(maybeSet "LANG" cfg.language.base)
// (maybeSet "LC_CTYPE" cfg.language.ctype)
// (maybeSet "LC_NUMERIC" cfg.language.numeric)
// (maybeSet "LC_TIME" cfg.language.time)
// (maybeSet "LC_COLLATE" cfg.language.collate)
// (maybeSet "LC_MONETARY" cfg.language.monetary)
// (maybeSet "LC_MESSAGES" cfg.language.messages)
// (maybeSet "LC_PAPER" cfg.language.paper)
// (maybeSet "LC_NAME" cfg.language.name)
// (maybeSet "LC_ADDRESS" cfg.language.address)
// (maybeSet "LC_TELEPHONE" cfg.language.telephone)
// (maybeSet "LC_MEASUREMENT" cfg.language.measurement);
# Provide a file holding all session variables.
home.sessionVariablesPackage = 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
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}
'' + lib.concatStringsSep "\n"
(lib.mapAttrsToList
(env: values: config.lib.shell.export
env
(config.lib.shell.prependToVar ":" env values))
cfg.sessionSearchVariables) + "\n"
${config.lib.shell.exportAll cfg.sessionVariables}
''
+ lib.concatStringsSep "\n" (
lib.mapAttrsToList (
env: values: config.lib.shell.export env (config.lib.shell.prependToVar ":" env values)
) cfg.sessionSearchVariables
)
+ "\n"
+ cfg.sessionVariablesExtra;
};
home.sessionSearchVariables.PATH =
lib.mkIf (cfg.sessionPath != [ ]) cfg.sessionPath;
home.sessionSearchVariables.PATH = lib.mkIf (cfg.sessionPath != [ ]) cfg.sessionPath;
home.packages = [ config.home.sessionVariablesPackage ];
@ -638,9 +642,8 @@ in
# In case the user has moved from a user-install of Home Manager
# to a submodule managed one we attempt to uninstall the
# `home-manager-path` package if it is installed.
home.activation.installPackages = lib.hm.dag.entryAfter ["writeBoundary"] (
if config.submoduleSupport.externalPackageInstall
then
home.activation.installPackages = lib.hm.dag.entryAfter [ "writeBoundary" ] (
if config.submoduleSupport.externalPackageInstall then
''
nixProfileRemove home-manager-path
''
@ -681,62 +684,66 @@ in
# in the `hm-modules` text domain.
lib.bash.initHomeManagerLib =
let
domainDir = pkgs.runCommand "hm-modules-messages" {
nativeBuildInputs = [ pkgs.buildPackages.gettext ];
} ''
for path in ${./po}/*.po; do
lang="''${path##*/}"
lang="''${lang%%.*}"
mkdir -p "$out/$lang/LC_MESSAGES"
msgfmt -o "$out/$lang/LC_MESSAGES/hm-modules.mo" "$path"
done
'';
domainDir =
pkgs.runCommand "hm-modules-messages"
{
nativeBuildInputs = [ pkgs.buildPackages.gettext ];
}
''
for path in ${./po}/*.po; do
lang="''${path##*/}"
lang="''${lang%%.*}"
mkdir -p "$out/$lang/LC_MESSAGES"
msgfmt -o "$out/$lang/LC_MESSAGES/hm-modules.mo" "$path"
done
'';
in
''
export TEXTDOMAIN=hm-modules
export TEXTDOMAINDIR=${domainDir}
source ${../lib/bash/home-manager.sh}
'';
''
export TEXTDOMAIN=hm-modules
export TEXTDOMAINDIR=${domainDir}
source ${../lib/bash/home-manager.sh}
'';
home.activationPackage =
let
mkCmd = res: ''
_iNote "Activating %s" "${res.name}"
${res.data}
'';
_iNote "Activating %s" "${res.name}"
${res.data}
'';
sortedCommands = lib.hm.dag.topoSort cfg.activation;
activationCmds =
if sortedCommands ? result then
lib.concatStringsSep "\n" (map mkCmd sortedCommands.result)
else
abort ("Dependency cycle in activation script: "
+ builtins.toJSON sortedCommands);
abort ("Dependency cycle in activation script: " + builtins.toJSON sortedCommands);
# Programs that always should be available on the activation
# script's PATH.
activationBinPaths = lib.makeBinPath (
with pkgs; [
bash
coreutils
diffutils # For `cmp` and `diff`.
findutils
gettext
gnugrep
gnused
jq
ncurses # For `tput`.
]
++ config.home.extraActivationPath
)
+ (
# Add path of the Nix binaries, if a Nix package is configured, then
# use that one, otherwise grab the path of the nix-env tool.
if config.nix.enable && config.nix.package != null then
":${config.nix.package}/bin"
else
":$(${pkgs.coreutils}/bin/dirname $(${pkgs.coreutils}/bin/readlink -m $(type -p nix-env)))"
)
+ lib.optionalString (!cfg.emptyActivationPath) "\${PATH:+:}$PATH";
activationBinPaths =
lib.makeBinPath (
with pkgs;
[
bash
coreutils
diffutils # For `cmp` and `diff`.
findutils
gettext
gnugrep
gnused
jq
ncurses # For `tput`.
]
++ config.home.extraActivationPath
)
+ (
# Add path of the Nix binaries, if a Nix package is configured, then
# use that one, otherwise grab the path of the nix-env tool.
if config.nix.enable && config.nix.package != null then
":${config.nix.package}/bin"
else
":$(${pkgs.coreutils}/bin/dirname $(${pkgs.coreutils}/bin/readlink -m $(type -p nix-env)))"
)
+ lib.optionalString (!cfg.emptyActivationPath) "\${PATH:+:}$PATH";
activationScript = pkgs.writeShellScript "activation-script" ''
set -eu
@ -770,29 +777,28 @@ in
''}
'';
in
pkgs.runCommand
"home-manager-generation"
{
preferLocalBuild = true;
}
''
mkdir -p $out
pkgs.runCommand "home-manager-generation"
{
preferLocalBuild = true;
}
''
mkdir -p $out
echo "${config.home.version.full}" > $out/hm-version
echo "${config.home.version.full}" > $out/hm-version
cp ${activationScript} $out/activate
cp ${activationScript} $out/activate
mkdir $out/bin
ln -s $out/activate $out/bin/home-manager-generation
mkdir $out/bin
ln -s $out/activate $out/bin/home-manager-generation
substituteInPlace $out/activate \
--subst-var-by GENERATION_DIR $out
substituteInPlace $out/activate \
--subst-var-by GENERATION_DIR $out
ln -s ${config.home-files} $out/home-files
ln -s ${cfg.path} $out/home-path
ln -s ${config.home-files} $out/home-files
ln -s ${cfg.path} $out/home-path
${cfg.extraBuilderCommands}
'';
${cfg.extraBuilderCommands}
'';
home.path = pkgs.buildEnv {
name = "home-manager-path";

View file

@ -1,33 +1,65 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
let
cfg = config.i18n.inputMethod;
gtk2Cache = pkgs.runCommandLocal "gtk2-immodule.cache" {
buildInputs = [ pkgs.gtk2 cfg.package ];
} ''
mkdir -p $out/etc/gtk-2.0/
GTK_PATH=${cfg.package}/lib/gtk-2.0/ \
gtk-query-immodules-2.0 > $out/etc/gtk-2.0/immodules.cache
'';
gtk2Cache =
pkgs.runCommandLocal "gtk2-immodule.cache"
{
buildInputs = [
pkgs.gtk2
cfg.package
];
}
''
mkdir -p $out/etc/gtk-2.0/
GTK_PATH=${cfg.package}/lib/gtk-2.0/ \
gtk-query-immodules-2.0 > $out/etc/gtk-2.0/immodules.cache
'';
gtk3Cache = pkgs.runCommandLocal "gtk3-immodule.cache" {
buildInputs = [ pkgs.gtk3 cfg.package ];
} ''
mkdir -p $out/etc/gtk-3.0/
GTK_PATH=${cfg.package}/lib/gtk-3.0/ \
gtk-query-immodules-3.0 > $out/etc/gtk-3.0/immodules.cache
'';
gtk3Cache =
pkgs.runCommandLocal "gtk3-immodule.cache"
{
buildInputs = [
pkgs.gtk3
cfg.package
];
}
''
mkdir -p $out/etc/gtk-3.0/
GTK_PATH=${cfg.package}/lib/gtk-3.0/ \
gtk-query-immodules-3.0 > $out/etc/gtk-3.0/immodules.cache
'';
in {
imports = [ ./fcitx5.nix ./hime.nix ./kime.nix ./nabi.nix ./uim.nix ];
in
{
imports = [
./fcitx5.nix
./hime.nix
./kime.nix
./nabi.nix
./uim.nix
];
options.i18n = {
inputMethod = {
enabled = lib.mkOption {
type = lib.types.nullOr
(lib.types.enum [ "fcitx" "fcitx5" "nabi" "uim" "hime" "kime" ]);
type = lib.types.nullOr (
lib.types.enum [
"fcitx"
"fcitx5"
"nabi"
"uim"
"hime"
"kime"
]
);
default = null;
example = "fcitx5";
description = ''
@ -73,15 +105,18 @@ in {
config = lib.mkIf (cfg.enabled != null) {
assertions = [
(lib.hm.assertions.assertPlatform "i18n.inputMethod" pkgs
lib.platforms.linux)
(lib.hm.assertions.assertPlatform "i18n.inputMethod" pkgs lib.platforms.linux)
{
assertion = cfg.enabled != "fcitx";
message = "fcitx has been removed, please use fcitx5 instead";
}
];
home.packages = [ cfg.package gtk2Cache gtk3Cache ];
home.packages = [
cfg.package
gtk2Cache
gtk3Cache
];
};
meta.maintainers = [ lib.hm.maintainers.kranzes ];

View file

@ -1,9 +1,15 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
let
im = config.i18n.inputMethod;
cfg = im.fcitx5;
fcitx5Package = cfg.fcitx5-with-addons.override { inherit (cfg) addons; };
in {
in
{
options = {
i18n.inputMethod.fcitx5 = {
fcitx5-with-addons = lib.mkOption {
@ -38,17 +44,18 @@ in {
i18n.inputMethod.package = fcitx5Package;
home = {
sessionVariables = {
GLFW_IM_MODULE = "ibus"; # IME support in kitty
SDL_IM_MODULE = "fcitx";
XMODIFIERS = "@im=fcitx";
} // lib.optionalAttrs (!cfg.waylandFrontend) {
GTK_IM_MODULE = "fcitx";
QT_IM_MODULE = "fcitx";
};
sessionVariables =
{
GLFW_IM_MODULE = "ibus"; # IME support in kitty
SDL_IM_MODULE = "fcitx";
XMODIFIERS = "@im=fcitx";
}
// lib.optionalAttrs (!cfg.waylandFrontend) {
GTK_IM_MODULE = "fcitx";
QT_IM_MODULE = "fcitx";
};
sessionSearchVariables.QT_PLUGIN_PATH =
[ "${fcitx5Package}/${pkgs.qt6.qtbase.qtPluginPrefix}" ];
sessionSearchVariables.QT_PLUGIN_PATH = [ "${fcitx5Package}/${pkgs.qt6.qtbase.qtPluginPrefix}" ];
};
systemd.user.services.fcitx5-daemon = {

View file

@ -1,4 +1,9 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
{
config = lib.mkIf (config.i18n.inputMethod.enabled == "hime") {

View file

@ -1,10 +1,22 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
let
inherit (lib) literalExpression mkIf mkOption mkRemovedOptionModule types;
inherit (lib)
literalExpression
mkIf
mkOption
mkRemovedOptionModule
types
;
cfg = config.i18n.inputMethod.kime;
in {
in
{
imports = [
(mkRemovedOptionModule [ "i18n" "inputMethod" "kime" "config" ] ''
Please use 'i18n.inputMethod.kime.extraConfig' instead.

View file

@ -1,4 +1,9 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
{
config = lib.mkIf (config.i18n.inputMethod.enabled == "nabi") {

View file

@ -1,13 +1,25 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
let cfg = config.i18n.inputMethod.uim;
in {
let
cfg = config.i18n.inputMethod.uim;
in
{
options = {
i18n.inputMethod.uim = {
toolbar = lib.mkOption {
type =
lib.types.enum [ "gtk" "gtk3" "gtk-systray" "gtk3-systray" "qt4" ];
type = lib.types.enum [
"gtk"
"gtk3"
"gtk-systray"
"gtk3-systray"
"qt4"
];
default = "gtk";
example = "gtk-systray";
description = ''
@ -32,11 +44,12 @@ in {
Description = "Uim input method editor";
PartOf = [ "graphical-session.desktop" ];
};
Service.ExecStart = toString
(pkgs.writeShellScript "start-uim-xim-and-uim-toolbar" ''
Service.ExecStart = toString (
pkgs.writeShellScript "start-uim-xim-and-uim-toolbar" ''
${pkgs.uim}/bin/uim-xim &
${pkgs.uim}/bin/uim-toolbar-${cfg.toolbar}
'');
''
);
Install.WantedBy = [ "graphical-session.target" ];
};
};

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (pkgs.stdenv.hostPlatform) isDarwin;
@ -8,45 +13,48 @@ let
labelPrefix = "org.nix-community.home.";
dstDir = "${config.home.homeDirectory}/Library/LaunchAgents";
launchdConfig = { config, name, ... }: {
options = {
enable = lib.mkEnableOption name;
config = lib.mkOption {
type = lib.types.submodule (import ./launchd.nix);
default = { };
example = lib.literalExpression ''
{
ProgramArguments = [ "/usr/bin/say" "Good afternoon" ];
StartCalendarInterval = [
{
Hour = 12;
Minute = 0;
}
];
}
'';
description = ''
Define a launchd job. See {manpage}`launchd.plist(5)` for details.
'';
launchdConfig =
{ config, name, ... }:
{
options = {
enable = lib.mkEnableOption name;
config = lib.mkOption {
type = lib.types.submodule (import ./launchd.nix);
default = { };
example = lib.literalExpression ''
{
ProgramArguments = [ "/usr/bin/say" "Good afternoon" ];
StartCalendarInterval = [
{
Hour = 12;
Minute = 0;
}
];
}
'';
description = ''
Define a launchd job. See {manpage}`launchd.plist(5)` for details.
'';
};
};
config = {
config.Label = lib.mkDefault "${labelPrefix}${name}";
};
};
config = { config.Label = lib.mkDefault "${labelPrefix}${name}"; };
};
toAgent = config: pkgs.writeText "${config.Label}.plist" (toPlist { } config);
agentPlists = lib.mapAttrs'
(n: v: lib.nameValuePair "${v.config.Label}.plist" (toAgent v.config))
(lib.filterAttrs (n: v: v.enable) cfg.agents);
agentPlists = lib.mapAttrs' (n: v: lib.nameValuePair "${v.config.Label}.plist" (toAgent v.config)) (
lib.filterAttrs (n: v: v.enable) cfg.agents
);
agentsDrv = pkgs.runCommand "home-manager-agents" { } ''
mkdir -p "$out"
declare -A plists
plists=(${
lib.concatStringsSep " "
(lib.mapAttrsToList (name: value: "['${name}']='${value}'") agentPlists)
lib.concatStringsSep " " (lib.mapAttrsToList (name: value: "['${name}']='${value}'") agentPlists)
})
for dest in "''${!plists[@]}"; do
@ -54,8 +62,12 @@ let
ln -s "$src" "$out/$dest"
done
'';
in {
meta.maintainers = with lib.maintainers; [ khaneliman midchildan ];
in
{
meta.maintainers = with lib.maintainers; [
khaneliman
midchildan
];
options.launchd = {
enable = lib.mkOption {
@ -77,12 +89,16 @@ in {
config = lib.mkMerge [
{
assertions = [{
assertion = (cfg.enable && agentPlists != { }) -> isDarwin;
message =
let names = lib.concatStringsSep ", " (lib.attrNames agentPlists);
in "Must use Darwin for modules that require Launchd: " + names;
}];
assertions = [
{
assertion = (cfg.enable && agentPlists != { }) -> isDarwin;
message =
let
names = lib.concatStringsSep ", " (lib.attrNames agentPlists);
in
"Must use Darwin for modules that require Launchd: " + names;
}
];
}
(lib.mkIf isDarwin {
@ -94,170 +110,170 @@ in {
# because it needs to be owned by the user running it.
home.activation.setupLaunchAgents =
lib.hm.dag.entryAfter [ "writeBoundary" ] # Bash
''
# Disable errexit to ensure we process all agents even if some fail
set +e
''
# Disable errexit to ensure we process all agents even if some fail
set +e
# Stop an agent if it's running
bootoutAgent() {
local domain="$1"
local agentName="$2"
# Stop an agent if it's running
bootoutAgent() {
local domain="$1"
local agentName="$2"
verboseEcho "Stopping agent '$domain/$agentName'..."
local bootout_output
bootout_output=$(run /bin/launchctl bootout "$domain/$agentName" 2>&1) || {
# Only show warning if it's not the common "No such process" error
if [[ "$bootout_output" != *"No such process"* ]]; then
warnEcho "Failed to stop agent '$domain/$agentName': $bootout_output"
else
verboseEcho "Agent '$domain/$agentName' was not running"
fi
verboseEcho "Stopping agent '$domain/$agentName'..."
local bootout_output
bootout_output=$(run /bin/launchctl bootout "$domain/$agentName" 2>&1) || {
# Only show warning if it's not the common "No such process" error
if [[ "$bootout_output" != *"No such process"* ]]; then
warnEcho "Failed to stop agent '$domain/$agentName': $bootout_output"
else
verboseEcho "Agent '$domain/$agentName' was not running"
fi
}
# Give the system a moment to fully unload the agent
sleep 1
}
# Give the system a moment to fully unload the agent
sleep 1
}
installAndBootstrapAgent() {
local srcPath="$1"
local dstPath="$2"
local domain="$3"
local agentName="$4"
installAndBootstrapAgent() {
local srcPath="$1"
local dstPath="$2"
local domain="$3"
local agentName="$4"
verboseEcho "Installing agent file to $dstPath"
if ! run install -Dm444 -T "$srcPath" "$dstPath"; then
errorEcho "Failed to install agent file for '$agentName'"
return 1
fi
verboseEcho "Starting agent '$domain/$agentName'"
local bootstrap_output
bootstrap_output=$(run /bin/launchctl bootstrap "$domain" "$dstPath" 2>&1) || {
local error_code=$?
if [[ "$bootstrap_output" == *"Bootstrap failed: 5: Input/output error"* ]]; then
errorEcho "Failed to start agent '$domain/$agentName' with I/O error (code 5)"
errorEcho "This typically happens when the agent wasn't unloaded before attempting to bootstrap the new agent."
else
errorEcho "Failed to start agent '$domain/$agentName' with error: $bootstrap_output"
verboseEcho "Installing agent file to $dstPath"
if ! run install -Dm444 -T "$srcPath" "$dstPath"; then
errorEcho "Failed to install agent file for '$agentName'"
return 1
fi
return 1
}
verboseEcho "Starting agent '$domain/$agentName'"
local bootstrap_output
bootstrap_output=$(run /bin/launchctl bootstrap "$domain" "$dstPath" 2>&1) || {
local error_code=$?
verboseEcho "Successfully started agent '$domain/$agentName'"
return 0
}
if [[ "$bootstrap_output" == *"Bootstrap failed: 5: Input/output error"* ]]; then
errorEcho "Failed to start agent '$domain/$agentName' with I/O error (code 5)"
errorEcho "This typically happens when the agent wasn't unloaded before attempting to bootstrap the new agent."
else
errorEcho "Failed to start agent '$domain/$agentName' with error: $bootstrap_output"
fi
processAgent() {
local srcPath="$1"
local dstDir="$2"
local domain="$3"
return 1
}
local agentFile="''${srcPath##*/}"
local agentName="''${agentFile%.plist}"
local dstPath="$dstDir/$agentFile"
# Skip if unchanged
if cmp -s "$srcPath" "$dstPath"; then
verboseEcho "Agent '$agentName' is already up-to-date"
verboseEcho "Successfully started agent '$domain/$agentName'"
return 0
fi
}
verboseEcho "Processing agent '$agentName'"
processAgent() {
local srcPath="$1"
local dstDir="$2"
local domain="$3"
# Stop/Unload agent if it's already running
if [[ -f "$dstPath" ]]; then
local agentFile="''${srcPath##*/}"
local agentName="''${agentFile%.plist}"
local dstPath="$dstDir/$agentFile"
# Skip if unchanged
if cmp -s "$srcPath" "$dstPath"; then
verboseEcho "Agent '$agentName' is already up-to-date"
return 0
fi
verboseEcho "Processing agent '$agentName'"
# Stop/Unload agent if it's already running
if [[ -f "$dstPath" ]]; then
bootoutAgent "$domain" "$agentName"
fi
installAndBootstrapAgent "$srcPath" "$dstPath" "$domain" "$agentName"
# Note: We continue processing even if this agent fails
return 0
}
removeAgent() {
local srcPath="$1"
local dstDir="$2"
local newDir="$3"
local domain="$4"
local agentFile="''${srcPath##*/}"
local agentName="''${agentFile%.plist}"
local dstPath="$dstDir/$agentFile"
if [[ -e "$newDir/$agentFile" ]]; then
verboseEcho "Agent '$agentName' still exists in new generation, skipping cleanup"
return 0
fi
if [[ ! -e "$dstPath" ]]; then
verboseEcho "Agent file '$dstPath' already removed"
return 0
fi
if ! cmp -s "$srcPath" "$dstPath"; then
warnEcho "Skipping deletion of '$dstPath', since its contents have diverged"
return 0
fi
# Stop and remove the agent
bootoutAgent "$domain" "$agentName"
fi
installAndBootstrapAgent "$srcPath" "$dstPath" "$domain" "$agentName"
# Note: We continue processing even if this agent fails
return 0
}
verboseEcho "Removing agent file '$dstPath'"
if run rm -f $VERBOSE_ARG "$dstPath"; then
verboseEcho "Successfully removed agent file for '$agentName'"
else
warnEcho "Failed to remove agent file '$dstPath'"
fi
removeAgent() {
local srcPath="$1"
local dstDir="$2"
local newDir="$3"
local domain="$4"
local agentFile="''${srcPath##*/}"
local agentName="''${agentFile%.plist}"
local dstPath="$dstDir/$agentFile"
if [[ -e "$newDir/$agentFile" ]]; then
verboseEcho "Agent '$agentName' still exists in new generation, skipping cleanup"
return 0
fi
}
if [[ ! -e "$dstPath" ]]; then
verboseEcho "Agent file '$dstPath' already removed"
return 0
fi
setupLaunchAgents() {
local oldDir newDir dstDir domain
if ! cmp -s "$srcPath" "$dstPath"; then
warnEcho "Skipping deletion of '$dstPath', since its contents have diverged"
return 0
fi
newDir="$(readlink -m "$newGenPath/LaunchAgents")"
dstDir=${lib.escapeShellArg dstDir}
domain="gui/$UID"
# Stop and remove the agent
bootoutAgent "$domain" "$agentName"
verboseEcho "Removing agent file '$dstPath'"
if run rm -f $VERBOSE_ARG "$dstPath"; then
verboseEcho "Successfully removed agent file for '$agentName'"
else
warnEcho "Failed to remove agent file '$dstPath'"
fi
return 0
}
setupLaunchAgents() {
local oldDir newDir dstDir domain
newDir="$(readlink -m "$newGenPath/LaunchAgents")"
dstDir=${lib.escapeShellArg dstDir}
domain="gui/$UID"
if [[ -n "''${oldGenPath:-}" ]]; then
oldDir="$(readlink -m "$oldGenPath/LaunchAgents")"
if [[ ! -d "$oldDir" ]]; then
verboseEcho "No previous LaunchAgents directory found"
if [[ -n "''${oldGenPath:-}" ]]; then
oldDir="$(readlink -m "$oldGenPath/LaunchAgents")"
if [[ ! -d "$oldDir" ]]; then
verboseEcho "No previous LaunchAgents directory found"
oldDir=""
fi
else
oldDir=""
fi
else
oldDir=""
verboseEcho "Setting up LaunchAgents in $dstDir"
[[ -d "$dstDir" ]] || run mkdir -p "$dstDir"
verboseEcho "Processing new/updated LaunchAgents..."
find -L "$newDir" -maxdepth 1 -name '*.plist' -type f | while read -r srcPath; do
processAgent "$srcPath" "$dstDir" "$domain"
done
# Skip cleanup if there's no previous generation
if [[ -z "$oldDir" || ! -d "$oldDir" ]]; then
verboseEcho "LaunchAgents setup complete"
return
fi
verboseEcho "Cleaning up removed LaunchAgents..."
find -L "$oldDir" -maxdepth 1 -name '*.plist' -type f | while read -r srcPath; do
removeAgent "$srcPath" "$dstDir" "$newDir" "$domain"
done
}
setupLaunchAgents
# Restore errexit
if [[ -o errexit ]]; then
set -e
fi
verboseEcho "Setting up LaunchAgents in $dstDir"
[[ -d "$dstDir" ]] || run mkdir -p "$dstDir"
verboseEcho "Processing new/updated LaunchAgents..."
find -L "$newDir" -maxdepth 1 -name '*.plist' -type f | while read -r srcPath; do
processAgent "$srcPath" "$dstDir" "$domain"
done
# Skip cleanup if there's no previous generation
if [[ -z "$oldDir" || ! -d "$oldDir" ]]; then
verboseEcho "LaunchAgents setup complete"
return
fi
verboseEcho "Cleaning up removed LaunchAgents..."
find -L "$oldDir" -maxdepth 1 -name '*.plist' -type f | while read -r srcPath; do
removeAgent "$srcPath" "$dstDir" "$newDir" "$domain"
done
}
setupLaunchAgents
# Restore errexit
if [[ -o errexit ]]; then
set -e
fi
'';
'';
})
];
}

View file

@ -29,7 +29,8 @@ let
inherit (lib) types mkOption; # added by Home Manager
launchdTypes = import ./types.nix { inherit config lib; };
in {
in
{
freeformType = with types; attrsOf anything; # added by Home Manager
options = {
@ -82,23 +83,27 @@ in {
inetdCompatibility = mkOption {
default = null;
example = { Wait = true; };
example = {
Wait = true;
};
description = ''
The presence of this key specifies that the daemon expects to be run as if it were launched from inetd.
'';
type = types.nullOr (types.submodule {
options = {
Wait = mkOption {
type = types.nullOr (types.either types.bool types.str);
default = null;
description = ''
This flag corresponds to the "wait" or "nowait" option of inetd. If true, then the listening
socket is passed via the standard in/out/error file descriptors. If false, then `accept(2)` is
called on behalf of the job, and the result is passed via the standard in/out/error descriptors.
'';
type = types.nullOr (
types.submodule {
options = {
Wait = mkOption {
type = types.nullOr (types.either types.bool types.str);
default = null;
description = ''
This flag corresponds to the "wait" or "nowait" option of inetd. If true, then the listening
socket is passed via the standard in/out/error file descriptors. If false, then `accept(2)` is
called on behalf of the job, and the result is passed via the standard in/out/error descriptors.
'';
};
};
};
});
}
);
};
LimitLoadToHosts = mkOption {
@ -120,7 +125,12 @@ in {
};
LimitLoadToSessionType = mkOption {
type = types.nullOr (types.oneOf [ types.str (types.listOf types.str) ]);
type = types.nullOr (
types.oneOf [
types.str
(types.listOf types.str)
]
);
default = null;
description = ''
This configuration file only applies to sessions of the type specified. This key is used in concert
@ -177,68 +187,72 @@ in {
};
KeepAlive = mkOption {
type = types.nullOr (types.either types.bool (types.submodule {
options = {
type = types.nullOr (
types.either types.bool (
types.submodule {
options = {
SuccessfulExit = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true, the job will be restarted as long as the program exits and with an exit status of zero.
If false, the job will be restarted in the inverse condition. This key implies that "RunAtLoad"
is set to true, since the job needs to run at least once before we can get an exit status.
'';
};
SuccessfulExit = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true, the job will be restarted as long as the program exits and with an exit status of zero.
If false, the job will be restarted in the inverse condition. This key implies that "RunAtLoad"
is set to true, since the job needs to run at least once before we can get an exit status.
'';
};
NetworkState = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true, the job will be kept alive as long as the network is up, where up is defined as at least
one non-loopback interface being up and having IPv4 or IPv6 addresses assigned to them. If
false, the job will be kept alive in the inverse condition.
'';
};
NetworkState = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true, the job will be kept alive as long as the network is up, where up is defined as at least
one non-loopback interface being up and having IPv4 or IPv6 addresses assigned to them. If
false, the job will be kept alive in the inverse condition.
'';
};
PathState = mkOption {
type = types.nullOr (types.attrsOf types.bool);
default = null;
description = ''
Each key in this dictionary is a file-system path. If the value of the key is true, then the job
will be kept alive as long as the path exists. If false, the job will be kept alive in the
inverse condition. The intent of this feature is that two or more jobs may create semaphores in
the file-system namespace.
'';
};
PathState = mkOption {
type = types.nullOr (types.attrsOf types.bool);
default = null;
description = ''
Each key in this dictionary is a file-system path. If the value of the key is true, then the job
will be kept alive as long as the path exists. If false, the job will be kept alive in the
inverse condition. The intent of this feature is that two or more jobs may create semaphores in
the file-system namespace.
'';
};
OtherJobEnabled = mkOption {
type = types.nullOr (types.attrsOf types.bool);
default = null;
description = ''
Each key in this dictionary is the label of another job. If the value of the key is true, then
this job is kept alive as long as that other job is enabled. Otherwise, if the value is false,
then this job is kept alive as long as the other job is disabled. This feature should not be
considered a substitute for the use of IPC.
'';
};
OtherJobEnabled = mkOption {
type = types.nullOr (types.attrsOf types.bool);
default = null;
description = ''
Each key in this dictionary is the label of another job. If the value of the key is true, then
this job is kept alive as long as that other job is enabled. Otherwise, if the value is false,
then this job is kept alive as long as the other job is disabled. This feature should not be
considered a substitute for the use of IPC.
'';
};
Crashed = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true, the the job will be restarted as long as it exited due to a signal which is typically
associated with a crash (SIGILL, SIGSEGV, etc.). If false, the job will be restarted in the
inverse condition.
'';
};
Crashed = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true, the the job will be restarted as long as it exited due to a signal which is typically
associated with a crash (SIGILL, SIGSEGV, etc.). If false, the job will be restarted in the
inverse condition.
'';
};
AfterInitialDemand = mkOption {
type = types.nullOr types.bool;
default = null;
};
AfterInitialDemand = mkOption {
type = types.nullOr types.bool;
default = null;
};
};
}));
};
}
)
);
default = null;
description = ''
This optional key is used to control whether your job is to be kept continuously running or to let
@ -371,10 +385,12 @@ in {
StartCalendarInterval = mkOption {
default = null;
example = [{
Hour = 2;
Minute = 30;
}];
example = [
{
Hour = 2;
Minute = 30;
}
];
description = ''
This optional key causes the job to be started every calendar interval as specified. The semantics are
much like {manpage}`crontab(5)`: Missing attributes are considered to be wildcard. Unlike cron which skips
@ -442,181 +458,187 @@ in {
Resource limits to be imposed on the job. These adjust variables set with `setrlimit(2)`. The following
keys apply:
'';
type = types.nullOr (types.submodule {
options = {
Core = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) core file that may be created.
'';
};
type = types.nullOr (
types.submodule {
options = {
Core = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) core file that may be created.
'';
};
CPU = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum amount of cpu time (in seconds) to be used by each process.
'';
};
CPU = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum amount of cpu time (in seconds) to be used by each process.
'';
};
Data = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the data segment for a process; this defines how far a program may
extend its break with the `sbrk(2)` system call.
'';
};
Data = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the data segment for a process; this defines how far a program may
extend its break with the `sbrk(2)` system call.
'';
};
FileSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) file that may be created.
'';
};
FileSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) file that may be created.
'';
};
MemoryLock = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) which a process may lock into memory using the mlock(2) function.
'';
};
MemoryLock = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) which a process may lock into memory using the mlock(2) function.
'';
};
NumberOfFiles = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of open files for this process. Setting this value in a system wide daemon
will set the `sysctl(3)` kern.maxfiles (SoftResourceLimits) or kern.maxfilesperproc (HardResourceLimits)
value in addition to the `setrlimit(2)` values.
'';
};
NumberOfFiles = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of open files for this process. Setting this value in a system wide daemon
will set the `sysctl(3)` kern.maxfiles (SoftResourceLimits) or kern.maxfilesperproc (HardResourceLimits)
value in addition to the `setrlimit(2)` values.
'';
};
NumberOfProcesses = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of simultaneous processes for this user id. Setting this value in a system
wide daemon will set the `sysctl(3)` kern.maxproc (SoftResourceLimits) or kern.maxprocperuid
(HardResourceLimits) value in addition to the `setrlimit(2)` values.
'';
};
NumberOfProcesses = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of simultaneous processes for this user id. Setting this value in a system
wide daemon will set the `sysctl(3)` kern.maxproc (SoftResourceLimits) or kern.maxprocperuid
(HardResourceLimits) value in addition to the `setrlimit(2)` values.
'';
};
ResidentSetSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) to which a process's resident set size may grow. This imposes a
limit on the amount of physical memory to be given to a process; if memory is tight, the system
will prefer to take memory from processes that are exceeding their declared resident set size.
'';
};
ResidentSetSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) to which a process's resident set size may grow. This imposes a
limit on the amount of physical memory to be given to a process; if memory is tight, the system
will prefer to take memory from processes that are exceeding their declared resident set size.
'';
};
Stack = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the stack segment for a process; this defines how far a program's
stack segment may be extended. Stack extension is performed automatically by the system.
'';
Stack = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the stack segment for a process; this defines how far a program's
stack segment may be extended. Stack extension is performed automatically by the system.
'';
};
};
};
});
}
);
};
HardResourceLimits = mkOption {
default = null;
example = { NumberOfFiles = 4096; };
example = {
NumberOfFiles = 4096;
};
description = ''
Resource limits to be imposed on the job. These adjust variables set with `setrlimit(2)`. The following
keys apply:
'';
type = types.nullOr (types.submodule {
options = {
Core = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) core file that may be created.
'';
};
type = types.nullOr (
types.submodule {
options = {
Core = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) core file that may be created.
'';
};
CPU = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum amount of cpu time (in seconds) to be used by each process.
'';
};
CPU = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum amount of cpu time (in seconds) to be used by each process.
'';
};
Data = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the data segment for a process; this defines how far a program may
extend its break with the `sbrk(2)` system call.
'';
};
Data = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the data segment for a process; this defines how far a program may
extend its break with the `sbrk(2)` system call.
'';
};
FileSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) file that may be created.
'';
};
FileSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The largest size (in bytes) file that may be created.
'';
};
MemoryLock = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) which a process may lock into memory using the `mlock(2)` function.
'';
};
MemoryLock = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) which a process may lock into memory using the `mlock(2)` function.
'';
};
NumberOfFiles = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of open files for this process. Setting this value in a system wide daemon
will set the `sysctl(3)` kern.maxfiles (SoftResourceLimits) or kern.maxfilesperproc (HardResourceLimits)
value in addition to the `setrlimit(2)` values.
'';
};
NumberOfFiles = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of open files for this process. Setting this value in a system wide daemon
will set the `sysctl(3)` kern.maxfiles (SoftResourceLimits) or kern.maxfilesperproc (HardResourceLimits)
value in addition to the `setrlimit(2)` values.
'';
};
NumberOfProcesses = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of simultaneous processes for this user id. Setting this value in a system
wide daemon will set the `sysctl(3)` kern.maxproc (SoftResourceLimits) or kern.maxprocperuid
(HardResourceLimits) value in addition to the `setrlimit(2)` values.
'';
};
NumberOfProcesses = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum number of simultaneous processes for this user id. Setting this value in a system
wide daemon will set the `sysctl(3)` kern.maxproc (SoftResourceLimits) or kern.maxprocperuid
(HardResourceLimits) value in addition to the `setrlimit(2)` values.
'';
};
ResidentSetSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) to which a process's resident set size may grow. This imposes a
limit on the amount of physical memory to be given to a process; if memory is tight, the system
will prefer to take memory from processes that are exceeding their declared resident set size.
'';
};
ResidentSetSize = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) to which a process's resident set size may grow. This imposes a
limit on the amount of physical memory to be given to a process; if memory is tight, the system
will prefer to take memory from processes that are exceeding their declared resident set size.
'';
};
Stack = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the stack segment for a process; this defines how far a program's
stack segment may be extended. Stack extension is performed automatically by the system.
'';
Stack = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
The maximum size (in bytes) of the stack segment for a process; this defines how far a program's
stack segment may be extended. Stack extension is performed automatically by the system.
'';
};
};
};
});
}
);
};
Nice = mkOption {
@ -628,8 +650,14 @@ in {
};
ProcessType = mkOption {
type = types.nullOr
(types.enum [ "Background" "Standard" "Adaptive" "Interactive" ]);
type = types.nullOr (
types.enum [
"Background"
"Standard"
"Adaptive"
"Interactive"
]
);
default = null;
example = "Background";
description = ''
@ -694,7 +722,11 @@ in {
MachServices = mkOption {
default = null;
example = { "org.nixos.service" = { ResetAtClose = true; }; };
example = {
"org.nixos.service" = {
ResetAtClose = true;
};
};
description = ''
This optional key is used to specify Mach services to be registered with the Mach bootstrap sub-system.
Each key in this dictionary should be the name of service to be advertised. The value of the key must
@ -703,32 +735,37 @@ in {
Finally, for the job itself, the values will be replaced with Mach ports at the time of check-in with
launchd.
'';
type = types.nullOr (types.attrsOf (types.either types.bool
(types.submodule {
options = {
ResetAtClose = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If this boolean is false, the port is recycled, thus leaving clients to remain oblivious to the
demand nature of job. If the value is set to true, clients receive port death notifications when
the job lets go of the receive right. The port will be recreated atomically with respect to bootstrap_look_up()
calls, so that clients can trust that after receiving a port death notification,
the new port will have already been recreated. Setting the value to true should be done with
care. Not all clients may be able to handle this behavior. The default value is false.
'';
};
type = types.nullOr (
types.attrsOf (
types.either types.bool (
types.submodule {
options = {
ResetAtClose = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If this boolean is false, the port is recycled, thus leaving clients to remain oblivious to the
demand nature of job. If the value is set to true, clients receive port death notifications when
the job lets go of the receive right. The port will be recreated atomically with respect to bootstrap_look_up()
calls, so that clients can trust that after receiving a port death notification,
the new port will have already been recreated. Setting the value to true should be done with
care. Not all clients may be able to handle this behavior. The default value is false.
'';
};
HideUntilCheckIn = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Reserve the name in the namespace, but cause bootstrap_look_up() to fail until the job has
checked in with launchd.
'';
};
};
})));
HideUntilCheckIn = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Reserve the name in the namespace, but cause bootstrap_look_up() to fail until the job has
checked in with launchd.
'';
};
};
}
)
)
);
};
LaunchEvents = mkOption {
@ -790,109 +827,123 @@ in {
The parameters below are used as inputs to call `getaddrinfo(3)`.
'';
type = types.nullOr (types.attrsOf (types.submodule {
options = {
SockType = mkOption {
type = types.nullOr (types.enum [ "stream" "dgram" "seqpacket" ]);
default = null;
description = ''
This optional key tells launchctl what type of socket to create. The default is "stream" and
other valid values for this key are "dgram" and "seqpacket" respectively.
'';
};
type = types.nullOr (
types.attrsOf (
types.submodule {
options = {
SockType = mkOption {
type = types.nullOr (
types.enum [
"stream"
"dgram"
"seqpacket"
]
);
default = null;
description = ''
This optional key tells launchctl what type of socket to create. The default is "stream" and
other valid values for this key are "dgram" and "seqpacket" respectively.
'';
};
SockPassive = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
This optional key specifies whether `listen(2)` or `connect(2)` should be called on the created file
descriptor. The default is true ("to listen").
'';
};
SockPassive = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
This optional key specifies whether `listen(2)` or `connect(2)` should be called on the created file
descriptor. The default is true ("to listen").
'';
};
SockNodeName = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key specifies the node to `connect(2)` or `bind(2)` to.
'';
};
SockNodeName = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key specifies the node to `connect(2)` or `bind(2)` to.
'';
};
SockServiceName = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key specifies the service on the node to `connect(2)` or `bind(2)` to.
'';
};
SockServiceName = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key specifies the service on the node to `connect(2)` or `bind(2)` to.
'';
};
SockFamily = mkOption {
type = types.nullOr (types.enum [ "IPv4" "IPv6" ]);
default = null;
description = ''
This optional key can be used to specifically request that "IPv4" or "IPv6" socket(s) be created.
'';
};
SockFamily = mkOption {
type = types.nullOr (
types.enum [
"IPv4"
"IPv6"
]
);
default = null;
description = ''
This optional key can be used to specifically request that "IPv4" or "IPv6" socket(s) be created.
'';
};
SockProtocol = mkOption {
type = types.nullOr (types.enum [ "TCP" ]);
default = null;
description = ''
This optional key specifies the protocol to be passed to `socket(2)`. The only value understood by
this key at the moment is "TCP".
'';
};
SockProtocol = mkOption {
type = types.nullOr (types.enum [ "TCP" ]);
default = null;
description = ''
This optional key specifies the protocol to be passed to `socket(2)`. The only value understood by
this key at the moment is "TCP".
'';
};
SockPathName = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
This optional key implies SockFamily is set to "Unix". It specifies the path to `connect(2)` or
`bind(2)` to.
'';
};
SockPathName = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
This optional key implies SockFamily is set to "Unix". It specifies the path to `connect(2)` or
`bind(2)` to.
'';
};
SecureSocketWithKey = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key is a variant of SockPathName. Instead of binding to a known path, a securely
generated socket is created and the path is assigned to the environment variable that is inherited
by all jobs spawned by launchd.
'';
};
SecureSocketWithKey = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key is a variant of SockPathName. Instead of binding to a known path, a securely
generated socket is created and the path is assigned to the environment variable that is inherited
by all jobs spawned by launchd.
'';
};
SockPathMode = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
This optional key specifies the mode of the socket. Known bug: Property lists don't support
octal, so please convert the value to decimal.
'';
};
SockPathMode = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
This optional key specifies the mode of the socket. Known bug: Property lists don't support
octal, so please convert the value to decimal.
'';
};
Bonjour = mkOption {
type =
types.nullOr (types.either types.bool (types.listOf types.str));
default = null;
description = ''
This optional key can be used to request that the service be registered with the
`mDNSResponder(8)`. If the value is boolean, the service name is inferred from the SockServiceName.
'';
};
Bonjour = mkOption {
type = types.nullOr (types.either types.bool (types.listOf types.str));
default = null;
description = ''
This optional key can be used to request that the service be registered with the
`mDNSResponder(8)`. If the value is boolean, the service name is inferred from the SockServiceName.
'';
};
MulticastGroup = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key can be used to request that the datagram socket join a multicast group. If the
value is a hostname, then `getaddrinfo(3)` will be used to join the correct multicast address for a
given socket family. If an explicit IPv4 or IPv6 address is given, it is required that the SockFamily
family also be set, otherwise the results are undefined.
'';
};
};
}));
MulticastGroup = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
This optional key can be used to request that the datagram socket join a multicast group. If the
value is a hostname, then `getaddrinfo(3)` will be used to join the correct multicast address for a
given socket family. If an explicit IPv4 or IPv6 address is given, it is required that the SockFamily
family also be set, otherwise the results are undefined.
'';
};
};
}
)
);
};
};

View file

@ -5,117 +5,146 @@
{ lib, ... }:
let
inherit (lib) imap1 types mkOption showOption mergeDefinitions;
inherit (builtins) map filter length deepSeq throw toString concatLists;
inherit (lib)
imap1
types
mkOption
showOption
mergeDefinitions
;
inherit (builtins)
map
filter
length
deepSeq
throw
toString
concatLists
;
inherit (lib.options) showDefs;
wildcardText = lib.literalMD "`*`";
/* *
A type of list which does not allow duplicate elements. The base/inner
list type to use (e.g. `types.listOf` or `types.nonEmptyListOf`) is passed
via argument `listType`, which must be the final type and not a function.
/*
*
A type of list which does not allow duplicate elements. The base/inner
list type to use (e.g. `types.listOf` or `types.nonEmptyListOf`) is passed
via argument `listType`, which must be the final type and not a function.
NOTE: The extra check for duplicates is quadratic and strict, so use this
type sparingly and only:
NOTE: The extra check for duplicates is quadratic and strict, so use this
type sparingly and only:
* when needed, and
* when the list is expected to be recursively short (e.g. < 10 elements)
and shallow (i.e. strict evaluation of the list won't take too long)
* when needed, and
* when the list is expected to be recursively short (e.g. < 10 elements)
and shallow (i.e. strict evaluation of the list won't take too long)
The implementation of this function is similar to that of
`types.nonEmptyListOf`.
The implementation of this function is similar to that of
`types.nonEmptyListOf`.
*/
types'.uniqueList = listType:
listType // {
description = "unique ${
types.optionDescriptionPhrase (class: class == "noun") listType
}";
types'.uniqueList =
listType:
listType
// {
description = "unique ${types.optionDescriptionPhrase (class: class == "noun") listType}";
substSubModules = m: types'.uniqueList (listType.substSubModules m);
# This has been taken from the implementation of `types.listOf`, but has
# been modified to throw on duplicates. This check cannot be done in the
# `check` fn as this check is deep/strict, and because `check` runs
# prior to merging.
merge = loc: defs:
merge =
loc: defs:
let
# Each element of `dupes` is a list. When there are duplicates,
# later lists will be duplicates of earlier lists, so just throw on
# the first set of duplicates found so that we don't have duplicate
# error msgs.
checked = filter (li:
checked = filter (
li:
if length li > 1 then
throw ''
The option `${
showOption loc
}' contains duplicate entries after merging:
The option `${showOption loc}' contains duplicate entries after merging:
${showDefs li}''
else
false) dupes;
dupes =
map (def: filter (def': def'.value == def.value) merged) merged;
merged = filter (x: x ? value) (concatLists (imap1 (n: def:
imap1 (m: el:
let
inherit (def) file;
loc' = loc
++ [ "[definition ${toString n}-entry ${toString m}]" ];
in (mergeDefinitions loc' listType.nestedTypes.elemType [{
inherit file;
value = el;
}]).optionalValue // {
inherit loc' file;
}) def.value) defs));
in deepSeq checked (map (x: x.value) merged);
false
) dupes;
dupes = map (def: filter (def': def'.value == def.value) merged) merged;
merged = filter (x: x ? value) (
concatLists (
imap1 (
n: def:
imap1 (
m: el:
let
inherit (def) file;
loc' = loc ++ [ "[definition ${toString n}-entry ${toString m}]" ];
in
(mergeDefinitions loc' listType.nestedTypes.elemType [
{
inherit file;
value = el;
}
]).optionalValue
// {
inherit loc' file;
}
) def.value
) defs
)
);
in
deepSeq checked (map (x: x.value) merged);
};
in {
StartCalendarInterval = let
CalendarIntervalEntry = types.submodule {
options = {
Minute = mkOption {
type = types.nullOr (types.ints.between 0 59);
default = null;
defaultText = wildcardText;
description = ''
The minute on which this job will be run.
'';
};
in
{
StartCalendarInterval =
let
CalendarIntervalEntry = types.submodule {
options = {
Minute = mkOption {
type = types.nullOr (types.ints.between 0 59);
default = null;
defaultText = wildcardText;
description = ''
The minute on which this job will be run.
'';
};
Hour = mkOption {
type = types.nullOr (types.ints.between 0 23);
default = null;
defaultText = wildcardText;
description = ''
The hour on which this job will be run.
'';
};
Hour = mkOption {
type = types.nullOr (types.ints.between 0 23);
default = null;
defaultText = wildcardText;
description = ''
The hour on which this job will be run.
'';
};
Day = mkOption {
type = types.nullOr (types.ints.between 1 31);
default = null;
defaultText = wildcardText;
description = ''
The day on which this job will be run.
'';
};
Day = mkOption {
type = types.nullOr (types.ints.between 1 31);
default = null;
defaultText = wildcardText;
description = ''
The day on which this job will be run.
'';
};
Weekday = mkOption {
type = types.nullOr (types.ints.between 0 7);
default = null;
defaultText = wildcardText;
description = ''
The weekday on which this job will be run (0 and 7 are Sunday).
'';
};
Weekday = mkOption {
type = types.nullOr (types.ints.between 0 7);
default = null;
defaultText = wildcardText;
description = ''
The weekday on which this job will be run (0 and 7 are Sunday).
'';
};
Month = mkOption {
type = types.nullOr (types.ints.between 1 12);
default = null;
defaultText = wildcardText;
description = ''
The month on which this job will be run.
'';
Month = mkOption {
type = types.nullOr (types.ints.between 1 12);
default = null;
defaultText = wildcardText;
description = ''
The month on which this job will be run.
'';
};
};
};
};
in types.either CalendarIntervalEntry
(types'.uniqueList (types.nonEmptyListOf CalendarIntervalEntry));
in
types.either CalendarIntervalEntry (types'.uniqueList (types.nonEmptyListOf CalendarIntervalEntry));
}

View file

@ -3,12 +3,13 @@
{
assertPlatform = module: pkgs: platforms: {
assertion = lib.elem pkgs.stdenv.hostPlatform.system platforms;
message = let
platformsStr = lib.concatStringsSep "\n"
(map (p: " - ${p}") (lib.sort (a: b: a < b) platforms));
in ''
The module ${module} does not support your platform. It only supports
message =
let
platformsStr = lib.concatStringsSep "\n" (map (p: " - ${p}") (lib.sort (a: b: a < b) platforms));
in
''
The module ${module} does not support your platform. It only supports
${platformsStr}'';
${platformsStr}'';
};
}

View file

@ -1,4 +1,5 @@
{ lib }: {
{ lib }:
{
# Converts a boolean to a yes/no string. This is used in lots of
# configuration formats.
yesNo = value: if value then "yes" else "no";

View file

@ -9,13 +9,23 @@
{ lib }:
let inherit (lib) all filterAttrs head hm mapAttrs length tail toposort;
in {
let
inherit (lib)
all
filterAttrs
head
hm
mapAttrs
length
tail
toposort
;
in
{
empty = { };
isEntry = e: e ? data && e ? after && e ? before;
isDag = dag:
builtins.isAttrs dag && all hm.dag.isEntry (builtins.attrValues dag);
isDag = dag: builtins.isAttrs dag && all hm.dag.isEntry (builtins.attrValues dag);
# Takes an attribute set containing entries built by entryAnywhere,
# entryAfter, and entryBefore to a topologically sorted list of
@ -73,11 +83,10 @@ in {
# ];
# }
# true
topoSort = dag:
topoSort =
dag:
let
dagBefore = dag: name:
builtins.attrNames
(filterAttrs (n: v: builtins.elem name v.before) dag);
dagBefore = dag: name: builtins.attrNames (filterAttrs (n: v: builtins.elem name v.before) dag);
normalizedDag = mapAttrs (n: v: {
name = n;
data = v.data;
@ -85,9 +94,12 @@ in {
}) dag;
before = a: b: builtins.elem a.name b.after;
sorted = toposort before (builtins.attrValues normalizedDag);
in if sorted ? result then {
result = map (v: { inherit (v) name data; }) sorted.result;
} else
in
if sorted ? result then
{
result = map (v: { inherit (v) name data; }) sorted.result;
}
else
sorted;
# Applies a function to each element of the given DAG.
@ -107,21 +119,28 @@ in {
#
# The entries as a whole can be given a relation to other DAG nodes. All
# generated nodes are then placed before or after those dependencies.
entriesBetween = tag:
entriesBetween =
tag:
let
go = i: before: after: entries:
go =
i: before: after: entries:
let
name = "${tag}-${toString i}";
i' = i + 1;
in if entries == [ ] then
in
if entries == [ ] then
hm.dag.empty
else if length entries == 1 then {
"${name}" = hm.dag.entryBetween before after (head entries);
} else
else if length entries == 1 then
{
"${name}" = hm.dag.entryBetween before after (head entries);
}
else
{
"${name}" = hm.dag.entryAfter after (head entries);
} // go (i + 1) before [ name ] (tail entries);
in go 0;
}
// go (i + 1) before [ name ] (tail entries);
in
go 0;
entriesAnywhere = tag: hm.dag.entriesBetween tag [ ] [ ];
entriesAfter = tag: hm.dag.entriesBetween tag [ ];

View file

@ -1,9 +1,22 @@
{ homeDirectory, lib, pkgs }:
{
homeDirectory,
lib,
pkgs,
}:
let
inherit (lib)
hasPrefix hm literalExpression mkDefault mkIf mkOption removePrefix types;
in {
hasPrefix
hm
literalExpression
mkDefault
mkIf
mkOption
removePrefix
types
;
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
@ -13,108 +26,121 @@ in {
# - opt the name of the option, for self-references
# - basePathDesc docbook compatible description of the base path
# - basePath the file base path
fileType = opt: basePathDesc: basePath:
types.attrsOf (types.submodule ({ name, config, ... }: {
options = {
enable = mkOption {
type = types.bool;
default = true;
description = ''
Whether this file should be generated. This option allows specific
files to be disabled.
'';
};
target = mkOption {
type = types.str;
apply = p:
let absPath = if hasPrefix "/" p then p else "${basePath}/${p}";
in removePrefix (homeDirectory + "/") absPath;
defaultText = literalExpression "name";
description = ''
Path to target file relative to ${basePathDesc}.
'';
};
fileType =
opt: basePathDesc: basePath:
types.attrsOf (
types.submodule (
{ name, config, ... }:
{
options = {
enable = mkOption {
type = types.bool;
default = true;
description = ''
Whether this file should be generated. This option allows specific
files to be disabled.
'';
};
target = mkOption {
type = types.str;
apply =
p:
let
absPath = if hasPrefix "/" p then p else "${basePath}/${p}";
in
removePrefix (homeDirectory + "/") absPath;
defaultText = literalExpression "name";
description = ''
Path to target file relative to ${basePathDesc}.
'';
};
text = mkOption {
default = null;
type = types.nullOr types.lines;
description = ''
Text of the file. If this option is null then
[](#opt-${opt}._name_.source)
must be set.
'';
};
text = mkOption {
default = null;
type = types.nullOr types.lines;
description = ''
Text of the file. If this option is null then
[](#opt-${opt}._name_.source)
must be set.
'';
};
source = mkOption {
type = types.path;
description = ''
Path of the source file or directory. If
[](#opt-${opt}._name_.text)
is non-null then this option will automatically point to a file
containing that text.
'';
};
source = mkOption {
type = types.path;
description = ''
Path of the source file or directory. If
[](#opt-${opt}._name_.text)
is non-null then this option will automatically point to a file
containing that text.
'';
};
executable = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Set the execute bit. If `null`, defaults to the mode
of the {var}`source` file or to `false`
for files created through the {var}`text` option.
'';
};
executable = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Set the execute bit. If `null`, defaults to the mode
of the {var}`source` file or to `false`
for files created through the {var}`text` 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.
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.
If `false` (the default) then the target
will be a symbolic link to the source directory. If
`true` 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.
'';
};
If `false` (the default) then the target
will be a symbolic link to the source directory. If
`true` 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.
'';
};
onChange = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands to run when file has changed between
generations. The script will be run
*after* the new files have been linked
into place.
onChange = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands to run when file has changed between
generations. The script will be run
*after* the new files have been linked
into place.
Note, this code is always run when `recursive` is
enabled.
'';
};
Note, this code is always run when `recursive` is
enabled.
'';
};
force = mkOption {
type = types.bool;
default = false;
description = ''
Whether the target path should be unconditionally replaced
by the managed file source. Warning, this will silently
delete the target regardless of whether it is a file or
link.
'';
};
};
force = mkOption {
type = types.bool;
default = false;
description = ''
Whether the target path should be unconditionally replaced
by the managed file source. Warning, this will silently
delete the target regardless of whether it is a file or
link.
'';
};
};
config = {
target = mkDefault name;
source = mkIf (config.text != null) (mkDefault (pkgs.writeTextFile {
inherit (config) text;
executable = config.executable == true; # can be null
name = hm.strings.storeFileName name;
}));
};
}));
config = {
target = mkDefault name;
source = mkIf (config.text != null) (
mkDefault (
pkgs.writeTextFile {
inherit (config) text;
executable = config.executable == true; # can be null
name = hm.strings.storeFileName name;
}
)
);
};
}
)
);
}

View file

@ -1,114 +1,158 @@
{ lib }:
{
toHyprconf = { attrs, indentLevel ? 0, importantPrefixes ? [ "$" ], }:
toHyprconf =
{
attrs,
indentLevel ? 0,
importantPrefixes ? [ "$" ],
}:
let
inherit (lib)
all concatMapStringsSep concatStrings concatStringsSep filterAttrs foldl
generators hasPrefix isAttrs isList mapAttrsToList replicate;
all
concatMapStringsSep
concatStrings
concatStringsSep
filterAttrs
foldl
generators
hasPrefix
isAttrs
isList
mapAttrsToList
replicate
;
initialIndent = concatStrings (replicate indentLevel " ");
toHyprconf' = indent: attrs:
toHyprconf' =
indent: attrs:
let
sections =
filterAttrs (n: v: isAttrs v || (isList v && all isAttrs v)) attrs;
sections = filterAttrs (n: v: isAttrs v || (isList v && all isAttrs v)) attrs;
mkSection = n: attrs:
mkSection =
n: attrs:
if lib.isList attrs then
(concatMapStringsSep "\n" (a: mkSection n a) attrs)
else ''
${indent}${n} {
${toHyprconf' " ${indent}" attrs}${indent}}
'';
else
''
${indent}${n} {
${toHyprconf' " ${indent}" attrs}${indent}}
'';
mkFields = generators.toKeyValue {
listsAsDuplicateKeys = true;
inherit indent;
};
allFields =
filterAttrs (n: v: !(isAttrs v || (isList v && all isAttrs v)))
attrs;
allFields = filterAttrs (n: v: !(isAttrs v || (isList v && all isAttrs v))) attrs;
isImportantField = n: _:
foldl (acc: prev: if hasPrefix prev n then true else acc) false
importantPrefixes;
isImportantField =
n: _: foldl (acc: prev: if hasPrefix prev n then true else acc) false importantPrefixes;
importantFields = filterAttrs isImportantField allFields;
fields = builtins.removeAttrs allFields
(mapAttrsToList (n: _: n) importantFields);
in mkFields importantFields
fields = builtins.removeAttrs allFields (mapAttrsToList (n: _: n) importantFields);
in
mkFields importantFields
+ concatStringsSep "\n" (mapAttrsToList mkSection sections)
+ mkFields fields;
in toHyprconf' initialIndent attrs;
in
toHyprconf' initialIndent attrs;
toKDL = { }:
toKDL =
{ }:
let
inherit (lib) concatStringsSep splitString mapAttrsToList any;
inherit (lib)
concatStringsSep
splitString
mapAttrsToList
any
;
inherit (builtins) typeOf replaceStrings elem;
# ListOf String -> String
indentStrings = let
# Although the input of this function is a list of strings,
# the strings themselves *will* contain newlines, so you need
# to normalize the list by joining and resplitting them.
unlines = lib.splitString "\n";
lines = lib.concatStringsSep "\n";
indentAll = lines: concatStringsSep "\n" (map (x: " " + x) lines);
in stringsWithNewlines: indentAll (unlines (lines stringsWithNewlines));
indentStrings =
let
# Although the input of this function is a list of strings,
# the strings themselves *will* contain newlines, so you need
# to normalize the list by joining and resplitting them.
unlines = lib.splitString "\n";
lines = lib.concatStringsSep "\n";
indentAll = lines: concatStringsSep "\n" (map (x: " " + x) lines);
in
stringsWithNewlines: indentAll (unlines (lines stringsWithNewlines));
# String -> String
sanitizeString = replaceStrings [ "\n" ''"'' ] [ "\\n" ''\"'' ];
# OneOf [Int Float String Bool Null] -> String
literalValueToString = element:
literalValueToString =
element:
lib.throwIfNot
(elem (typeOf element) [ "int" "float" "string" "bool" "null" ])
"Cannot convert value of type ${typeOf element} to KDL literal."
(if typeOf element == "null" then
"null"
else if element == false then
"false"
else if element == true then
"true"
else if typeOf element == "string" then
''"${sanitizeString element}"''
else
toString element);
(elem (typeOf element) [
"int"
"float"
"string"
"bool"
"null"
])
"Cannot convert value of type ${typeOf element} to KDL literal."
(
if typeOf element == "null" then
"null"
else if element == false then
"false"
else if element == true then
"true"
else if typeOf element == "string" then
''"${sanitizeString element}"''
else
toString element
);
# Attrset Conversion
# String -> AttrsOf Anything -> String
convertAttrsToKDL = name: attrs:
convertAttrsToKDL =
name: attrs:
let
optArgsString = lib.optionalString (attrs ? "_args")
(lib.pipe attrs._args [
optArgsString = lib.optionalString (attrs ? "_args") (
lib.pipe attrs._args [
(map literalValueToString)
(lib.concatStringsSep " ")
(s: s + " ")
]);
]
);
optPropsString = lib.optionalString (attrs ? "_props")
(lib.pipe attrs._props [
(lib.mapAttrsToList
(name: value: "${name}=${literalValueToString value}"))
optPropsString = lib.optionalString (attrs ? "_props") (
lib.pipe attrs._props [
(lib.mapAttrsToList (name: value: "${name}=${literalValueToString value}"))
(lib.concatStringsSep " ")
(s: s + " ")
]);
]
);
children =
lib.filterAttrs (name: _: !(elem name [ "_args" "_props" ])) attrs;
in ''
children = lib.filterAttrs (
name: _:
!(elem name [
"_args"
"_props"
])
) attrs;
in
''
${name} ${optArgsString}${optPropsString}{
${indentStrings (mapAttrsToList convertAttributeToKDL children)}
}'';
# List Conversion
# String -> ListOf (OneOf [Int Float String Bool Null]) -> String
convertListOfFlatAttrsToKDL = name: list:
let flatElements = map literalValueToString list;
in "${name} ${concatStringsSep " " flatElements}";
convertListOfFlatAttrsToKDL =
name: list:
let
flatElements = map literalValueToString list;
in
"${name} ${concatStringsSep " " flatElements}";
# String -> ListOf Anything -> String
convertListOfNonFlatAttrsToKDL = name: list: ''
@ -117,18 +161,39 @@
}'';
# String -> ListOf Anything -> String
convertListToKDL = name: list:
let elementsAreFlat = !any (el: elem (typeOf el) [ "list" "set" ]) list;
in if elementsAreFlat then
convertListToKDL =
name: list:
let
elementsAreFlat =
!any (
el:
elem (typeOf el) [
"list"
"set"
]
) list;
in
if elementsAreFlat then
convertListOfFlatAttrsToKDL name list
else
convertListOfNonFlatAttrsToKDL name list;
# Combined Conversion
# String -> Anything -> String
convertAttributeToKDL = name: value:
let vType = typeOf value;
in if elem vType [ "int" "float" "bool" "null" "string" ] then
convertAttributeToKDL =
name: value:
let
vType = typeOf value;
in
if
elem vType [
"int"
"float"
"bool"
"null"
"string"
]
then
"${name} ${literalValueToString value}"
else if vType == "set" then
convertAttrsToKDL name value
@ -139,99 +204,153 @@
Cannot convert type `(${typeOf value})` to KDL:
${name} = ${toString value}
'';
in attrs: ''
in
attrs: ''
${concatStringsSep "\n" (mapAttrsToList convertAttributeToKDL attrs)}
'';
toSCFG = { }:
toSCFG =
{ }:
let
inherit (lib) concatStringsSep mapAttrsToList any;
inherit (builtins) typeOf replaceStrings elem;
# ListOf String -> String
indentStrings = let
# Although the input of this function is a list of strings,
# the strings themselves *will* contain newlines, so you need
# to normalize the list by joining and resplitting them.
unlines = lib.splitString "\n";
lines = lib.concatStringsSep "\n";
indentAll = lines: concatStringsSep "\n" (map (x: " " + x) lines);
in stringsWithNewlines: indentAll (unlines (lines stringsWithNewlines));
indentStrings =
let
# Although the input of this function is a list of strings,
# the strings themselves *will* contain newlines, so you need
# to normalize the list by joining and resplitting them.
unlines = lib.splitString "\n";
lines = lib.concatStringsSep "\n";
indentAll = lines: concatStringsSep "\n" (map (x: " " + x) lines);
in
stringsWithNewlines: indentAll (unlines (lines stringsWithNewlines));
# String -> Bool
specialChars = s:
any (char: elem char (reserved ++ [ " " "'" "{" "}" ]))
(lib.stringToCharacters s);
specialChars =
s:
any (
char:
elem char (
reserved
++ [
" "
"'"
"{"
"}"
]
)
) (lib.stringToCharacters s);
# String -> String
sanitizeString =
replaceStrings reserved [ ''\"'' "\\\\" "\\r" "\\n" "\\t" ];
sanitizeString = replaceStrings reserved [
''\"''
"\\\\"
"\\r"
"\\n"
"\\t"
];
reserved = [ ''"'' "\\" "\r" "\n" " " ];
reserved = [
''"''
"\\"
"\r"
"\n"
" "
];
# OneOf [Int Float String Bool] -> String
literalValueToString = element:
lib.throwIfNot (elem (typeOf element) [ "int" "float" "string" "bool" ])
"Cannot convert value of type ${typeOf element} to SCFG literal."
(if element == false then
"false"
else if element == true then
"true"
else if typeOf element == "string" then
if element == "" || specialChars element then
''"${sanitizeString element}"''
else
element
else
toString element);
literalValueToString =
element:
lib.throwIfNot
(elem (typeOf element) [
"int"
"float"
"string"
"bool"
])
"Cannot convert value of type ${typeOf element} to SCFG literal."
(
if element == false then
"false"
else if element == true then
"true"
else if typeOf element == "string" then
if element == "" || specialChars element then ''"${sanitizeString element}"'' else element
else
toString element
);
# Bool -> ListOf (OneOf [Int Float String Bool]) -> String
toOptParamsString = cond: list:
lib.optionalString (cond) (lib.pipe list [
(map literalValueToString)
(concatStringsSep " ")
(s: " " + s)
]);
toOptParamsString =
cond: list:
lib.optionalString (cond) (
lib.pipe list [
(map literalValueToString)
(concatStringsSep " ")
(s: " " + s)
]
);
# Attrset Conversion
# String -> AttrsOf Anything -> String
convertAttrsToSCFG = name: attrs:
convertAttrsToSCFG =
name: attrs:
let
optParamsString = toOptParamsString (attrs ? "_params") attrs._params;
in ''
in
''
${name}${optParamsString} {
${indentStrings (convertToAttrsSCFG' attrs)}
}'';
# Attrset Conversion
# AttrsOf Anything -> ListOf String
convertToAttrsSCFG' = attrs:
mapAttrsToList convertAttributeToSCFG
(lib.filterAttrs (name: val: !isNull val && name != "_params") attrs);
convertToAttrsSCFG' =
attrs:
mapAttrsToList convertAttributeToSCFG (
lib.filterAttrs (name: val: !isNull val && name != "_params") attrs
);
# List Conversion
# String -> ListOf (OneOf [Int Float String Bool]) -> String
convertListOfFlatAttrsToSCFG = name: list:
let optParamsString = toOptParamsString (list != [ ]) list;
in "${name}${optParamsString}";
convertListOfFlatAttrsToSCFG =
name: list:
let
optParamsString = toOptParamsString (list != [ ]) list;
in
"${name}${optParamsString}";
# Combined Conversion
# String -> Anything -> String
convertAttributeToSCFG = name: value:
lib.throwIf (name == "") "Directive must not be empty"
(let vType = typeOf value;
in if elem vType [ "int" "float" "bool" "string" ] then
"${name} ${literalValueToString value}"
else if vType == "set" then
convertAttrsToSCFG name value
else if vType == "list" then
convertListOfFlatAttrsToSCFG name value
else
throw ''
Cannot convert type `(${typeOf value})` to SCFG:
${name} = ${toString value}
'');
in attrs:
convertAttributeToSCFG =
name: value:
lib.throwIf (name == "") "Directive must not be empty" (
let
vType = typeOf value;
in
if
elem vType [
"int"
"float"
"bool"
"string"
]
then
"${name} ${literalValueToString value}"
else if vType == "set" then
convertAttrsToSCFG name value
else if vType == "list" then
convertListOfFlatAttrsToSCFG name value
else
throw ''
Cannot convert type `(${typeOf value})` to SCFG:
${name} = ${toString value}
''
);
in
attrs:
lib.optionalString (attrs != { }) ''
${concatStringsSep "\n" (convertToAttrsSCFG' attrs)}
'';

View file

@ -7,7 +7,13 @@
let
inherit (lib)
concatMapStringsSep concatStrings escape hasPrefix head replaceStrings;
concatMapStringsSep
concatStrings
escape
hasPrefix
head
replaceStrings
;
mkPrimitive = t: v: {
_type = "gvariant";
@ -36,7 +42,8 @@ let
# Returns the GVariant type of a given Nix value. If no type can be
# found for the value then the empty string is returned.
typeOf = v:
typeOf =
v:
with type;
if builtins.isBool v then
boolean
@ -47,29 +54,27 @@ let
else if builtins.isString v then
string
else if builtins.isList v then
let elemType = elemTypeOf v;
in if elemType == "" then "" else arrayOf elemType
let
elemType = elemTypeOf v;
in
if elemType == "" then "" else arrayOf elemType
else if builtins.isAttrs v && v ? type then
v.type
else
"";
elemTypeOf = vs:
if builtins.isList vs then
if vs == [ ] then "" else typeOf (head vs)
else
"";
elemTypeOf = vs: if builtins.isList vs then if vs == [ ] then "" else typeOf (head vs) else "";
mkMaybe = elemType: elem:
mkPrimitive (type.maybeOf elemType) elem // {
__toString = self:
if self.value == null then
"@${self.type} nothing"
else
"just ${toString self.value}";
mkMaybe =
elemType: elem:
mkPrimitive (type.maybeOf elemType) elem
// {
__toString =
self: if self.value == null then "@${self.type} nothing" else "just ${toString self.value}";
};
in rec {
in
rec {
inherit type typeOf;
@ -83,7 +88,8 @@ in rec {
# Returns the GVariant value that most closely matches the given Nix
# value. If no GVariant value can be found then `null` is returned.
mkValue = v:
mkValue =
v:
if builtins.isBool v then
mkBoolean v
else if builtins.isInt v then
@ -99,55 +105,77 @@ in rec {
else
null;
mkArray = elemType: elems:
mkPrimitive (type.arrayOf elemType) (map mkValue elems) // {
__toString = self:
"@${self.type} [${concatMapStringsSep "," toString self.value}]";
mkArray =
elemType: elems:
mkPrimitive (type.arrayOf elemType) (map mkValue elems)
// {
__toString = self: "@${self.type} [${concatMapStringsSep "," toString self.value}]";
};
mkEmptyArray = elemType: mkArray elemType [ ];
mkVariant = elem:
let gvarElem = mkValue elem;
in mkPrimitive type.variant gvarElem // {
mkVariant =
elem:
let
gvarElem = mkValue elem;
in
mkPrimitive type.variant gvarElem
// {
__toString = self: "@${self.type} <${toString self.value}>";
};
mkDictionaryEntry = elems:
mkDictionaryEntry =
elems:
let
gvarElems = map mkValue elems;
dictionaryType = type.dictionaryEntryOf (map (e: e.type) gvarElems);
in mkPrimitive dictionaryType gvarElems // {
__toString = self:
"@${self.type} {${concatMapStringsSep "," toString self.value}}";
in
mkPrimitive dictionaryType gvarElems
// {
__toString = self: "@${self.type} {${concatMapStringsSep "," toString self.value}}";
};
mkNothing = elemType: mkMaybe elemType null;
mkJust = elem: let gvarElem = mkValue elem; in mkMaybe gvarElem.type gvarElem;
mkJust =
elem:
let
gvarElem = mkValue elem;
in
mkMaybe gvarElem.type gvarElem;
mkTuple = elems:
mkTuple =
elems:
let
gvarElems = map mkValue elems;
tupleType = type.tupleOf (map (e: e.type) gvarElems);
in mkPrimitive tupleType gvarElems // {
__toString = self:
"@${self.type} (${concatMapStringsSep "," toString self.value})";
in
mkPrimitive tupleType gvarElems
// {
__toString = self: "@${self.type} (${concatMapStringsSep "," toString self.value})";
};
mkBoolean = v:
mkPrimitive type.boolean v // {
mkBoolean =
v:
mkPrimitive type.boolean v
// {
__toString = self: if self.value then "true" else "false";
};
mkString = v:
let sanitize = s: replaceStrings [ "\n" ] [ "\\n" ] (escape [ "'" "\\" ] s);
in mkPrimitive type.string v // {
mkString =
v:
let
sanitize = s: replaceStrings [ "\n" ] [ "\\n" ] (escape [ "'" "\\" ] s);
in
mkPrimitive type.string v
// {
__toString = self: "'${sanitize self.value}'";
};
mkObjectpath = v:
mkPrimitive type.string v // {
mkObjectpath =
v:
mkPrimitive type.string v
// {
__toString = self: "objectpath '${escape [ "'" ] self.value}'";
};
@ -157,8 +185,10 @@ in rec {
mkUint16 = mkPrimitive type.uint16;
mkInt32 = v:
mkPrimitive type.int32 v // {
mkInt32 =
v:
mkPrimitive type.int32 v
// {
__toString = self: toString self.value;
};
@ -168,8 +198,10 @@ in rec {
mkUint64 = mkPrimitive type.uint64;
mkDouble = v:
mkPrimitive type.double v // {
mkDouble =
v:
mkPrimitive type.double v
// {
__toString = self: toString self.value;
};

View file

@ -107,10 +107,12 @@
github = "d-dervishi";
githubId = 61125355;
name = "David Dervishi";
keys = [{
longKeyId = "rsa4096/0xB1C012F0E7697195";
fingerprint = "4C92 E3B0 21B5 5562 A1E0 CE3D B1C0 12F0 E769 7195";
}];
keys = [
{
longKeyId = "rsa4096/0xB1C012F0E7697195";
fingerprint = "4C92 E3B0 21B5 5562 A1E0 CE3D B1C0 12F0 E769 7195";
}
];
};
Dines97 = {
name = "Denis Kaynar";
@ -147,8 +149,7 @@
email = "yiheng.he@proton.me";
matrix = "@hey2022:matrix.org";
github = "hey2022";
keys =
[{ fingerprint = "128E 09C0 6F73 D678 6BB5 E551 5EA5 3C75 F7BE 3EDE"; }];
keys = [ { fingerprint = "128E 09C0 6F73 D678 6BB5 E551 5EA5 3C75 F7BE 3EDE"; } ];
};
jack5079 = {
name = "Jack W.";
@ -167,10 +168,12 @@
name = "Jessica";
email = "jess+nix@jessie.cafe";
githubId = 43591752;
keys = [{
longkeyid = "rsa3072/0xBA3350686C918606";
fingerprint = "8092 3BD1 ECD0 E436 671D C8E9 BA33 5068 6C91 8606";
}];
keys = [
{
longkeyid = "rsa3072/0xBA3350686C918606";
fingerprint = "8092 3BD1 ECD0 E436 671D C8E9 BA33 5068 6C91 8606";
}
];
};
jkarlson = {
email = "jekarlson@gmail.com";
@ -250,10 +253,12 @@
email = "kamadorueda@gmail.com";
github = "kamadorueda";
githubId = 47480384;
keys = [{
longkeyid = "rsa4096/0x04D0CEAF916A9A40";
fingerprint = "2BE3 BAFD 793E A349 ED1F F00F 04D0 CEAF 916A 9A40";
}];
keys = [
{
longkeyid = "rsa4096/0x04D0CEAF916A9A40";
fingerprint = "2BE3 BAFD 793E A349 ED1F F00F 04D0 CEAF 916A 9A40";
}
];
};
katexochen = {
name = "Paul Meyer";
@ -339,20 +344,24 @@
email = "nick@hassan.host";
github = "n-hass";
githubId = 72363381;
keys = [{
longkeyid = "rsa4096/0xFC95AB946A781EE7";
fingerprint = "FDEE 6116 DBA7 8840 7323 4466 A371 5973 2728 A6A6";
}];
keys = [
{
longkeyid = "rsa4096/0xFC95AB946A781EE7";
fingerprint = "FDEE 6116 DBA7 8840 7323 4466 A371 5973 2728 A6A6";
}
];
};
seylerius = {
email = "sable@seyleri.us";
name = "Sable Seyler";
github = "seylerius";
githubId = 1145981;
keys = [{
logkeyid = "rsa4096/0x68BF2EAE6D91CAFF";
fingerprint = "F0E0 0311 126A CD72 4392 25E6 68BF 2EAE 6D91 CAFF";
}];
keys = [
{
logkeyid = "rsa4096/0x68BF2EAE6D91CAFF";
fingerprint = "F0E0 0311 126A CD72 4392 25E6 68BF 2EAE 6D91 CAFF";
}
];
};
silmarp = {
name = "Silmar Pereira da Silva Junior";
@ -394,10 +403,12 @@
github = "msfjarvis";
githubId = "13348378";
name = "Harsh Shandilya";
keys = [{
longkeyid = "rsa4096/0xB7843F823355E9B9";
fingerprint = "8F87 050B 0F9C B841 1515 7399 B784 3F82 3355 E9B9";
}];
keys = [
{
longkeyid = "rsa4096/0xB7843F823355E9B9";
fingerprint = "8F87 050B 0F9C B841 1515 7399 B784 3F82 3355 E9B9";
}
];
};
ambroisie = {
email = "bruno.home-manager@belanyi.fr";
@ -571,10 +582,12 @@
email = "88944439+rcerc@users.noreply.github.com";
github = "rcerc";
githubId = 88944439;
keys = [{
longkeyid = "ed25519/0x3F98EC7EC2B87ED1";
fingerprint = "D5D6 FD1F 0D9A 3284 FB9B C26D 3F98 EC7E C2B8 7ED1";
}];
keys = [
{
longkeyid = "ed25519/0x3F98EC7EC2B87ED1";
fingerprint = "D5D6 FD1F 0D9A 3284 FB9B C26D 3F98 EC7E C2B8 7ED1";
}
];
};
mtoohey = {
name = "Matthew Toohey";
@ -594,8 +607,7 @@
matrix = "@soywod:matrix.org";
github = "soywod";
githubId = 10437171;
keys =
[{ fingerprint = "75F0 AB7C FE01 D077 AEE6 CAFD 353E 4A18 EE0F AB72"; }];
keys = [ { fingerprint = "75F0 AB7C FE01 D077 AEE6 CAFD 353E 4A18 EE0F AB72"; } ];
};
tensor5 = {
github = "tensor5";
@ -615,8 +627,7 @@
github = "toastal";
githubId = 561087;
name = "toastal";
keys =
[{ fingerprint = "7944 74B7 D236 DAB9 C9EF E7F9 5CCE 6F14 66D4 7C9E"; }];
keys = [ { fingerprint = "7944 74B7 D236 DAB9 C9EF E7F9 5CCE 6F14 66D4 7C9E"; } ];
};
tomodachi94 = {
email = "tomodachi94+nixpkgs@protonmail.com";
@ -683,8 +694,7 @@
email = "git+nix@cleslie.uk";
github = "callumio";
githubId = 16057677;
keys =
[{ fingerprint = "BC82 4BB5 1656 D144 285E A0EC D382 C4AF EECE AA90"; }];
keys = [ { fingerprint = "BC82 4BB5 1656 D144 285E A0EC D382 C4AF EECE AA90"; } ];
};
ALameLlama = {
name = "Nicholas Ciechanowski";
@ -703,10 +713,12 @@
email = "me@hpsaucii.dev";
github = "HPsaucii";
githubId = 126502193;
keys = [{
longkeyid = "rsa4096/0xEDB2C634166AE6AD";
fingerprint = "AD32 73D4 5E0E 9478 E826 543F EDB2 C634 166A E6AD";
}];
keys = [
{
longkeyid = "rsa4096/0xEDB2C634166AE6AD";
fingerprint = "AD32 73D4 5E0E 9478 E826 543F EDB2 C634 166A E6AD";
}
];
};
folliehiyuki = {
name = "Hoang Nguyen";

View file

@ -1,66 +1,85 @@
{ lib }: rec {
{ lib }:
rec {
mkNushellInline = expr: lib.setType "nushell-inline" { inherit expr; };
isNushellInline = lib.isType "nushell-inline";
toNushell = { indent ? "", multiline ? true, asBindings ? false, }@args:
toNushell =
{
indent ? "",
multiline ? true,
asBindings ? false,
}@args:
v:
let
innerIndent = "${indent} ";
introSpace = if multiline then ''
introSpace =
if multiline then
''
${innerIndent}'' else
" ";
outroSpace = if multiline then ''
${innerIndent}''
else
" ";
outroSpace =
if multiline then
''
${indent}'' else
" ";
${indent}''
else
" ";
innerArgs = args // {
indent = if asBindings then indent else innerIndent;
asBindings = false;
};
concatItems = lib.concatStringsSep introSpace;
generatedBindings = assert lib.assertMsg (badVarNames == [ ])
"Bad Nushell variable names: ${
lib.generators.toPretty { } badVarNames
}";
lib.concatStrings (lib.mapAttrsToList (key: value: ''
${indent}let ${key} = ${toNushell innerArgs value}
'') v);
generatedBindings =
assert lib.assertMsg (badVarNames == [ ])
"Bad Nushell variable names: ${lib.generators.toPretty { } badVarNames}";
lib.concatStrings (
lib.mapAttrsToList (key: value: ''
${indent}let ${key} = ${toNushell innerArgs value}
'') v
);
isBadVarName = name:
isBadVarName =
name:
# Extracted from https://github.com/nushell/nushell/blob/ebc7b80c23f777f70c5053cca428226b3fe00d30/crates/nu-parser/src/parser.rs#L33
# Variables with numeric or even empty names are allowed. The only requisite is not containing any of the following characters
let invalidVariableCharacters = ".[({+-*^/=!<>&|";
in lib.match "^[$]?[^${lib.escapeRegex invalidVariableCharacters}]+$"
name == null;
let
invalidVariableCharacters = ".[({+-*^/=!<>&|";
in
lib.match "^[$]?[^${lib.escapeRegex invalidVariableCharacters}]+$" name == null;
badVarNames = lib.filter isBadVarName (builtins.attrNames v);
in if asBindings then
in
if asBindings then
generatedBindings
else if v == null then
"null"
else if lib.isInt v || lib.isFloat v || lib.isString v || lib.isBool v then
lib.strings.toJSON v
else if lib.isList v then
(if v == [ ] then
"[]"
else
"[${introSpace}${
concatItems (map (value: "${toNushell innerArgs value}") v)
}${outroSpace}]")
(
if v == [ ] then
"[]"
else
"[${introSpace}${concatItems (map (value: "${toNushell innerArgs value}") v)}${outroSpace}]"
)
else if lib.isAttrs v then
(if isNushellInline v then
"(${v.expr})"
else if v == { } then
"{}"
else if lib.isDerivation v then
toString v
else
"{${introSpace}${
concatItems (lib.mapAttrsToList (key: value:
"${lib.strings.toJSON key}: ${toNushell innerArgs value}") v)
}${outroSpace}}")
(
if isNushellInline v then
"(${v.expr})"
else if v == { } then
"{}"
else if lib.isDerivation v then
toString v
else
"{${introSpace}${
concatItems (
lib.mapAttrsToList (key: value: "${lib.strings.toJSON key}: ${toNushell innerArgs value}") v
)
}${outroSpace}}"
)
else
abort "nushell.toNushell: type ${lib.typeOf v} is unsupported";
}

View file

@ -2,27 +2,35 @@
let
mkShellIntegrationOption = name:
{ config, baseName ? name, extraDescription ? "" }:
let attrName = "enable${baseName}Integration";
in lib.mkOption {
mkShellIntegrationOption =
name:
{
config,
baseName ? name,
extraDescription ? "",
}:
let
attrName = "enable${baseName}Integration";
in
lib.mkOption {
default = config.home.shell.${attrName};
defaultText = lib.literalMD "[](#opt-home.shell.${attrName})";
example = false;
description = "Whether to enable ${name} integration.${
lib.optionalString (extraDescription != "")
("\n\n" + extraDescription)
}";
lib.optionalString (extraDescription != "") ("\n\n" + extraDescription)
}";
type = lib.types.bool;
};
in rec {
in
rec {
# Produces a Bourne shell like statement that prepend new values to
# an possibly existing variable, using sep(arator).
# Example:
# prependToVar ":" "PATH" [ "$HOME/bin" "$HOME/.local/bin" ]
# => "$HOME/bin:$HOME/.local/bin:${PATH:+:}\$PATH"
prependToVar = sep: n: v:
prependToVar =
sep: n: v:
"${lib.concatStringsSep sep v}\${${n}:+${sep}}\$${n}";
# Produces a Bourne shell like variable export statement.

View file

@ -3,11 +3,15 @@
nixpkgsLib:
let mkHmLib = import ./.;
in nixpkgsLib.extend (self: super: {
hm = mkHmLib { lib = self; };
let
mkHmLib = import ./.;
in
nixpkgsLib.extend (
self: super: {
hm = mkHmLib { lib = self; };
# For forward compatibility.
literalExpression = super.literalExpression or super.literalExample;
literalDocBook = super.literalDocBook or super.literalExample;
})
# For forward compatibility.
literalExpression = super.literalExpression or super.literalExample;
literalDocBook = super.literalDocBook or super.literalExample;
}
)

View file

@ -2,22 +2,39 @@
let
inherit (lib)
genList length lowerChars replaceStrings stringToCharacters upperChars;
in {
genList
length
lowerChars
replaceStrings
stringToCharacters
upperChars
;
in
{
# Figures out a valid Nix store name for the given path.
storeFileName = 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
safeChars =
[
"+"
"."
"_"
"?"
"="
]
++ lowerChars
++ upperChars
++ stringToCharacters "0123456789";
empties = l: genList (x: "") (length l);
unsafeInName =
stringToCharacters (replaceStrings safeChars (empties safeChars) path);
unsafeInName = stringToCharacters (replaceStrings safeChars (empties safeChars) path);
safeName = replaceStrings unsafeInName (empties unsafeInName) path;
in "hm_" + safeName;
in
"hm_" + safeName;
}

View file

@ -2,42 +2,65 @@
let
inherit (lib)
concatStringsSep defaultFunctor fixedWidthNumber hm imap1 isAttrs isList
length listToAttrs mapAttrs mkIf mkOrder mkOption mkOptionType nameValuePair
stringLength types warn;
concatStringsSep
defaultFunctor
fixedWidthNumber
hm
imap1
isAttrs
isList
length
listToAttrs
mapAttrs
mkIf
mkOrder
mkOption
mkOptionType
nameValuePair
stringLength
types
warn
;
dagEntryOf = elemType:
dagEntryOf =
elemType:
let
submoduleType = types.submodule ({ name, ... }: {
options = {
data = mkOption { type = elemType; };
after = mkOption { type = with types; listOf str; };
before = mkOption { type = with types; listOf str; };
};
config = mkIf (elemType.name == "submodule") {
data._module.args.dagName = name;
};
});
maybeConvert = def:
submoduleType = types.submodule (
{ name, ... }:
{
options = {
data = mkOption { type = elemType; };
after = mkOption { type = with types; listOf str; };
before = mkOption { type = with types; listOf str; };
};
config = mkIf (elemType.name == "submodule") {
data._module.args.dagName = name;
};
}
);
maybeConvert =
def:
if hm.dag.isEntry def.value then
def.value
else
hm.dag.entryAnywhere (if def ? priority then
mkOrder def.priority def.value
else
def.value);
in mkOptionType {
hm.dag.entryAnywhere (if def ? priority then mkOrder def.priority def.value else def.value);
in
mkOptionType {
name = "dagEntryOf";
description = "DAG entry of ${elemType.description}";
# leave the checking to the submodule type
merge = loc: defs:
submoduleType.merge loc (map (def: {
inherit (def) file;
value = maybeConvert def;
}) defs);
merge =
loc: defs:
submoduleType.merge loc (
map (def: {
inherit (def) file;
value = maybeConvert def;
}) defs
);
};
in rec {
in
rec {
# A directed acyclic graph of some inner type.
#
# Note, if the element type is a submodule then the `name` argument
@ -45,16 +68,21 @@ in rec {
# internal structure of the DAG values. To give access to the
# "actual" attribute name a new submodule argument is provided with
# the name `dagName`.
dagOf = elemType:
let attrEquivalent = types.attrsOf (dagEntryOf elemType);
in mkOptionType rec {
dagOf =
elemType:
let
attrEquivalent = types.attrsOf (dagEntryOf elemType);
in
mkOptionType rec {
name = "dagOf";
description = "DAG of ${elemType.description}";
inherit (attrEquivalent) check merge emptyValue;
getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "<name>" ]);
getSubModules = elemType.getSubModules;
substSubModules = m: dagOf (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; };
functor = (defaultFunctor name) // {
wrapped = elemType;
};
nestedTypes.elemType = elemType;
};
}

View file

@ -1,10 +1,28 @@
{ lib, gvariant ? import ./gvariant.nix { inherit lib; } }:
{
lib,
gvariant ? import ./gvariant.nix { inherit lib; },
}:
let
inherit (lib)
all concatMap foldl' getFiles getValues head isFunction literalExpression
mergeAttrs mergeDefaultOption mergeOneOption mergeOptions mkOption
mkOptionType showFiles showOption types;
all
concatMap
foldl'
getFiles
getValues
head
isFunction
literalExpression
mergeAttrs
mergeDefaultOption
mergeOneOption
mergeOptions
mkOption
mkOptionType
showFiles
showOption
types
;
typesDag = import ./types-dag.nix { inherit lib; };
@ -12,24 +30,30 @@ let
# must refer back to the type.
gvar = gvariant;
in rec {
in
rec {
inherit (typesDag) dagOf;
selectorFunction = mkOptionType {
name = "selectorFunction";
description = "Function that takes an attribute set and returns a list"
description =
"Function that takes an attribute set and returns a list"
+ " containing a selection of the values of the input set";
check = isFunction;
merge = _loc: defs: as: concatMap (select: select as) (getValues defs);
merge =
_loc: defs: as:
concatMap (select: select as) (getValues defs);
};
overlayFunction = mkOptionType {
name = "overlayFunction";
description = "An overlay function, takes self and super and returns"
description =
"An overlay function, takes self and super and returns"
+ " an attribute set overriding the desired attributes.";
check = isFunction;
merge = _loc: defs: self: super:
merge =
_loc: defs: self: super:
foldl' (res: def: mergeAttrs res (def.value self super)) { } defs;
};
@ -69,28 +93,34 @@ in rec {
name = "gvariant";
description = "GVariant value";
check = v: gvar.mkValue v != null;
merge = loc: defs:
merge =
loc: defs:
let
vdefs = map (d:
d // {
value =
if gvar.isGVariant d.value then d.value else gvar.mkValue d.value;
}) defs;
vdefs = map (
d:
d
// {
value = if gvar.isGVariant d.value then d.value else gvar.mkValue d.value;
}
) defs;
vals = map (d: d.value) vdefs;
defTypes = map (x: x.type) vals;
sameOrNull = x: y: if x == y then y else null;
# A bit naive to just check the first entry…
sharedDefType = foldl' sameOrNull (head defTypes) defTypes;
allChecked = all (x: check x) vals;
in if sharedDefType == null then
throw ("Cannot merge definitions of `${showOption loc}' with"
in
if sharedDefType == null then
throw (
"Cannot merge definitions of `${showOption loc}' with"
+ " mismatched GVariant types given in"
+ " ${showFiles (getFiles defs)}.")
+ " ${showFiles (getFiles defs)}."
)
else if gvar.isArray sharedDefType && allChecked then
gvar.mkValue ((types.listOf gvariant).merge loc
(map (d: d // { value = d.value.value; }) vdefs)) // {
type = sharedDefType;
}
gvar.mkValue ((types.listOf gvariant).merge loc (map (d: d // { value = d.value.value; }) vdefs))
// {
type = sharedDefType;
}
else if gvar.isTuple sharedDefType && allChecked then
mergeOneOption loc defs
else if gvar.isMaybe sharedDefType && allChecked then
@ -107,27 +137,37 @@ in rec {
mergeDefaultOption loc defs;
};
nushellValue = let
valueType = types.nullOr (types.oneOf [
(lib.mkOptionType {
name = "nushell";
description = "Nushell inline value";
descriptionClass = "name";
check = lib.isType "nushell-inline";
})
types.bool
types.int
types.float
types.str
types.path
(types.attrsOf valueType // {
description = "attribute set of Nushell values";
descriptionClass = "name";
})
(types.listOf valueType // {
description = "list of Nushell values";
descriptionClass = "name";
})
]);
in valueType;
nushellValue =
let
valueType = types.nullOr (
types.oneOf [
(lib.mkOptionType {
name = "nushell";
description = "Nushell inline value";
descriptionClass = "name";
check = lib.isType "nushell-inline";
})
types.bool
types.int
types.float
types.str
types.path
(
types.attrsOf valueType
// {
description = "attribute set of Nushell values";
descriptionClass = "name";
}
)
(
types.listOf valueType
// {
description = "list of Nushell values";
descriptionClass = "name";
}
)
]
);
in
valueType;
}

View file

@ -2,7 +2,8 @@
rec {
# Produces a Zsh shell like value
toZshValue = v:
toZshValue =
v:
if builtins.isBool v then
if v then "true" else "false"
else if builtins.isString v then

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
@ -9,7 +14,8 @@ let
inherit (config.home.version) release isReleaseBranch;
};
in {
in
{
options = {
manual.html.enable = lib.mkOption {
type = lib.types.bool;
@ -51,7 +57,10 @@ in {
config = {
home.packages = lib.mkMerge [
(lib.mkIf cfg.html.enable [ docs.manual.html docs.manual.htmlOpenTool ])
(lib.mkIf cfg.html.enable [
docs.manual.html
docs.manual.htmlOpenTool
])
(lib.mkIf cfg.manpages.enable [ docs.manPages ])
(lib.mkIf cfg.json.enable [ docs.options.json ])
];

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) types;
@ -7,18 +12,23 @@ let
toDconfIni = lib.generators.toINI { mkKeyValue = mkIniKeyValue; };
mkIniKeyValue = key: value:
"${key}=${toString (lib.hm.gvariant.mkValue value)}";
mkIniKeyValue = key: value: "${key}=${toString (lib.hm.gvariant.mkValue value)}";
# The dconf keys managed by this configuration. We store this as part of the
# generation state to be able to reset keys that become unmanaged during
# switch.
stateDconfKeys = pkgs.writeText "dconf-keys.json" (builtins.toJSON
(lib.concatLists (lib.mapAttrsToList
(dir: entries: lib.mapAttrsToList (key: _: "/${dir}/${key}") entries)
cfg.settings)));
stateDconfKeys = pkgs.writeText "dconf-keys.json" (
builtins.toJSON (
lib.concatLists (
lib.mapAttrsToList (
dir: entries: lib.mapAttrsToList (key: _: "/${dir}/${key}") entries
) cfg.settings
)
)
);
in {
in
{
meta.maintainers = [ lib.maintainers.rycee ];
options = {
@ -84,8 +94,8 @@ in {
ln -s ${stateDconfKeys} $out/state/${stateDconfKeys.name}
'';
home.activation.dconfSettings = lib.hm.dag.entryAfter [ "installPackages" ]
(let
home.activation.dconfSettings = lib.hm.dag.entryAfter [ "installPackages" ] (
let
iniFile = pkgs.writeText "hm-dconf.ini" (toDconfIni cfg.settings);
statePath = "state/${stateDconfKeys.name}";
@ -95,7 +105,12 @@ in {
${config.lib.bash.initHomeManagerLib}
PATH=${lib.makeBinPath [ pkgs.dconf pkgs.jq ]}''${PATH:+:}$PATH
PATH=${
lib.makeBinPath [
pkgs.dconf
pkgs.jq
]
}''${PATH:+:}$PATH
oldState="$1"
newState="$2"
@ -116,7 +131,8 @@ in {
run $DCONF_DBUS_RUN_SESSION dconf reset "$key"
done
'';
in ''
in
''
if [[ -v DBUS_SESSION_BUS_ADDRESS ]]; then
export DCONF_DBUS_RUN_SESSION=""
else
@ -132,6 +148,7 @@ in {
run $DCONF_DBUS_RUN_SESSION ${pkgs.dconf}/bin/dconf load / < ${iniFile}
unset DCONF_DBUS_RUN_SESSION
'');
''
);
};
}

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
@ -6,7 +11,8 @@ let
iniFormat = pkgs.formats.ini { };
in {
in
{
meta.maintainers = with lib.maintainers; [ loicreynier ];
options.editorconfig = {
@ -38,14 +44,18 @@ in {
};
config = lib.mkIf (cfg.enable && cfg.settings != { }) {
home.file.".editorconfig".text = let
renderedSettings = lib.generators.toINIWithGlobalSection { } {
globalSection = { root = true; };
sections = cfg.settings;
};
in ''
# Generated by Home Manager
${renderedSettings}
'';
home.file.".editorconfig".text =
let
renderedSettings = lib.generators.toINIWithGlobalSection { } {
globalSection = {
root = true;
};
sections = cfg.settings;
};
in
''
# Generated by Home Manager
${renderedSettings}
'';
};
}

View file

@ -2,7 +2,12 @@
#
# https://github.com/NixOS/nixpkgs/blob/23.11/nixos/modules/config/fonts/fontconfig.nix
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
@ -10,15 +15,19 @@ let
profileDirectory = config.home.profileDirectory;
in {
in
{
meta.maintainers = [ lib.maintainers.rycee ];
imports = [
(lib.mkRenamedOptionModule [ "fonts" "fontconfig" "enableProfileFonts" ] [
"fonts"
"fontconfig"
"enable"
])
(lib.mkRenamedOptionModule
[ "fonts" "fontconfig" "enableProfileFonts" ]
[
"fonts"
"fontconfig"
"enable"
]
)
];
options = {
@ -117,53 +126,58 @@ in {
fi
'';
xdg.configFile = let
mkFontconfigConf = conf: ''
<?xml version='1.0'?>
xdg.configFile =
let
mkFontconfigConf = conf: ''
<?xml version='1.0'?>
<!-- Generated by Home Manager. -->
<!-- Generated by Home Manager. -->
<!DOCTYPE fontconfig SYSTEM 'urn:fontconfig:fonts.dtd'>
<fontconfig>
${conf}
</fontconfig>
'';
in {
"fontconfig/conf.d/10-hm-fonts.conf".text = mkFontconfigConf ''
<description>Add fonts in the Nix user profile</description>
<!DOCTYPE fontconfig SYSTEM 'urn:fontconfig:fonts.dtd'>
<fontconfig>
${conf}
</fontconfig>
'';
in
{
"fontconfig/conf.d/10-hm-fonts.conf".text = mkFontconfigConf ''
<description>Add fonts in the Nix user profile</description>
<include ignore_missing="yes">${config.home.path}/etc/fonts/conf.d</include>
<include ignore_missing="yes">${config.home.path}/etc/fonts/fonts.conf</include>
<include ignore_missing="yes">${config.home.path}/etc/fonts/conf.d</include>
<include ignore_missing="yes">${config.home.path}/etc/fonts/fonts.conf</include>
<dir>${config.home.path}/lib/X11/fonts</dir>
<dir>${config.home.path}/share/fonts</dir>
<dir>${profileDirectory}/lib/X11/fonts</dir>
<dir>${profileDirectory}/share/fonts</dir>
<dir>${config.home.path}/lib/X11/fonts</dir>
<dir>${config.home.path}/share/fonts</dir>
<dir>${profileDirectory}/lib/X11/fonts</dir>
<dir>${profileDirectory}/share/fonts</dir>
<cachedir>${config.home.path}/lib/fontconfig/cache</cachedir>
'';
<cachedir>${config.home.path}/lib/fontconfig/cache</cachedir>
'';
"fontconfig/conf.d/52-hm-default-fonts.conf".text = let
genDefault = fonts: name:
lib.optionalString (fonts != [ ]) ''
<alias binding="same">
<family>${name}</family>
<prefer>
${
lib.concatStringsSep "" (map (font: ''
<family>${font}</family>
'') fonts)
}
</prefer>
</alias>
"fontconfig/conf.d/52-hm-default-fonts.conf".text =
let
genDefault =
fonts: name:
lib.optionalString (fonts != [ ]) ''
<alias binding="same">
<family>${name}</family>
<prefer>
${lib.concatStringsSep "" (
map (font: ''
<family>${font}</family>
'') fonts
)}
</prefer>
</alias>
'';
in
mkFontconfigConf ''
<!-- Default fonts -->
${genDefault cfg.defaultFonts.sansSerif "sans-serif"}
${genDefault cfg.defaultFonts.serif "serif"}
${genDefault cfg.defaultFonts.monospace "monospace"}
${genDefault cfg.defaultFonts.emoji "emoji"}
'';
in mkFontconfigConf ''
<!-- Default fonts -->
${genDefault cfg.defaultFonts.sansSerif "sans-serif"}
${genDefault cfg.defaultFonts.serif "serif"}
${genDefault cfg.defaultFonts.monospace "monospace"}
${genDefault cfg.defaultFonts.emoji "emoji"}
'';
};
};
};
}

View file

@ -1,7 +1,12 @@
{ config, lib, ... }:
let
inherit (lib) literalExpression mkOption optionalAttrs types;
inherit (lib)
literalExpression
mkOption
optionalAttrs
types
;
cfg = config.gtk;
cfg2 = config.gtk.gtk2;
@ -9,22 +14,26 @@ let
cfg4 = config.gtk.gtk4;
toGtk3Ini = lib.generators.toINI {
mkKeyValue = key: value:
mkKeyValue =
key: value:
let
value' =
if lib.isBool value then lib.boolToString value else toString value;
in "${lib.escape [ "=" ] key}=${value'}";
value' = if lib.isBool value then lib.boolToString value else toString value;
in
"${lib.escape [ "=" ] key}=${value'}";
};
formatGtk2Option = n: v:
formatGtk2Option =
n: v:
let
v' = if lib.isBool v then
lib.boolToString lib.value
else if lib.isString v then
''"${v}"''
else
toString v;
in "${lib.escape [ "=" ] n} = ${v'}";
v' =
if lib.isBool v then
lib.boolToString lib.value
else if lib.isString v then
''"${v}"''
else
toString v;
in
"${lib.escape [ "=" ] n} = ${v'}";
themeType = types.submodule {
options = {
@ -100,7 +109,8 @@ let
};
};
in {
in
{
meta.maintainers = [ lib.maintainers.rycee ];
imports = [
@ -153,10 +163,8 @@ in {
configLocation = mkOption {
type = types.path;
default = "${config.home.homeDirectory}/.gtkrc-2.0";
defaultText =
literalExpression ''"''${config.home.homeDirectory}/.gtkrc-2.0"'';
example =
literalExpression ''"''${config.xdg.configHome}/gtk-2.0/gtkrc"'';
defaultText = literalExpression ''"''${config.home.homeDirectory}/.gtkrc-2.0"'';
example = literalExpression ''"''${config.xdg.configHome}/gtk-2.0/gtkrc"'';
description = ''
The location to put the GTK configuration file.
'';
@ -172,7 +180,13 @@ in {
};
extraConfig = mkOption {
type = with types; attrsOf (oneOf [ bool int str ]);
type =
with types;
attrsOf (oneOf [
bool
int
str
]);
default = { };
example = {
gtk-cursor-blink = false;
@ -220,75 +234,86 @@ in {
};
};
config = lib.mkIf cfg.enable (let
gtkIni = optionalAttrs (cfg.font != null) {
gtk-font-name =
let fontSize = if cfg.font.size != null then cfg.font.size else 10;
in "${cfg.font.name} ${toString fontSize}";
} // optionalAttrs (cfg.theme != null) { gtk-theme-name = cfg.theme.name; }
// optionalAttrs (cfg.iconTheme != null) {
gtk-icon-theme-name = cfg.iconTheme.name;
} // optionalAttrs (cfg.cursorTheme != null) {
gtk-cursor-theme-name = cfg.cursorTheme.name;
} // optionalAttrs
(cfg.cursorTheme != null && cfg.cursorTheme.size != null) {
gtk-cursor-theme-size = cfg.cursorTheme.size;
config = lib.mkIf cfg.enable (
let
gtkIni =
optionalAttrs (cfg.font != null) {
gtk-font-name =
let
fontSize = if cfg.font.size != null then cfg.font.size else 10;
in
"${cfg.font.name} ${toString fontSize}";
}
// optionalAttrs (cfg.theme != null) { gtk-theme-name = cfg.theme.name; }
// optionalAttrs (cfg.iconTheme != null) {
gtk-icon-theme-name = cfg.iconTheme.name;
}
// optionalAttrs (cfg.cursorTheme != null) {
gtk-cursor-theme-name = cfg.cursorTheme.name;
}
// optionalAttrs (cfg.cursorTheme != null && cfg.cursorTheme.size != null) {
gtk-cursor-theme-size = cfg.cursorTheme.size;
};
gtk4Css =
lib.optionalString (cfg.theme != null && cfg.theme.package != null) ''
/**
* GTK 4 reads the theme configured by gtk-theme-name, but ignores it.
* It does however respect user CSS, so import the theme from here.
**/
@import url("file://${cfg.theme.package}/share/themes/${cfg.theme.name}/gtk-4.0/gtk.css");
''
+ cfg4.extraCss;
dconfIni =
optionalAttrs (cfg.font != null) {
font-name =
let
fontSize = if cfg.font.size != null then cfg.font.size else 10;
in
"${cfg.font.name} ${toString fontSize}";
}
// optionalAttrs (cfg.theme != null) { gtk-theme = cfg.theme.name; }
// optionalAttrs (cfg.iconTheme != null) {
icon-theme = cfg.iconTheme.name;
}
// optionalAttrs (cfg.cursorTheme != null) {
cursor-theme = cfg.cursorTheme.name;
}
// optionalAttrs (cfg.cursorTheme != null && cfg.cursorTheme.size != null) {
cursor-size = cfg.cursorTheme.size;
};
optionalPackage = opt: lib.optional (opt != null && opt.package != null) opt.package;
in
{
home.packages = lib.concatMap optionalPackage [
cfg.font
cfg.theme
cfg.iconTheme
cfg.cursorTheme
];
home.file.${cfg2.configLocation}.text =
lib.concatMapStrings (l: l + "\n") (lib.mapAttrsToList formatGtk2Option gtkIni)
+ cfg2.extraConfig
+ "\n";
home.sessionVariables.GTK2_RC_FILES = cfg2.configLocation;
xdg.configFile."gtk-3.0/settings.ini".text = toGtk3Ini { Settings = gtkIni // cfg3.extraConfig; };
xdg.configFile."gtk-3.0/gtk.css" = lib.mkIf (cfg3.extraCss != "") { text = cfg3.extraCss; };
xdg.configFile."gtk-3.0/bookmarks" = lib.mkIf (cfg3.bookmarks != [ ]) {
text = lib.concatMapStrings (l: l + "\n") cfg3.bookmarks;
};
gtk4Css =
lib.optionalString (cfg.theme != null && cfg.theme.package != null) ''
/**
* GTK 4 reads the theme configured by gtk-theme-name, but ignores it.
* It does however respect user CSS, so import the theme from here.
**/
@import url("file://${cfg.theme.package}/share/themes/${cfg.theme.name}/gtk-4.0/gtk.css");
'' + cfg4.extraCss;
xdg.configFile."gtk-4.0/settings.ini".text = toGtk3Ini { Settings = gtkIni // cfg4.extraConfig; };
dconfIni = optionalAttrs (cfg.font != null) {
font-name =
let fontSize = if cfg.font.size != null then cfg.font.size else 10;
in "${cfg.font.name} ${toString fontSize}";
} // optionalAttrs (cfg.theme != null) { gtk-theme = cfg.theme.name; }
// optionalAttrs (cfg.iconTheme != null) {
icon-theme = cfg.iconTheme.name;
} // optionalAttrs (cfg.cursorTheme != null) {
cursor-theme = cfg.cursorTheme.name;
} // optionalAttrs
(cfg.cursorTheme != null && cfg.cursorTheme.size != null) {
cursor-size = cfg.cursorTheme.size;
};
xdg.configFile."gtk-4.0/gtk.css" = lib.mkIf (gtk4Css != "") { text = gtk4Css; };
optionalPackage = opt:
lib.optional (opt != null && opt.package != null) opt.package;
in {
home.packages = lib.concatMap optionalPackage [
cfg.font
cfg.theme
cfg.iconTheme
cfg.cursorTheme
];
home.file.${cfg2.configLocation}.text = lib.concatMapStrings (l: l + "\n")
(lib.mapAttrsToList formatGtk2Option gtkIni) + cfg2.extraConfig + "\n";
home.sessionVariables.GTK2_RC_FILES = cfg2.configLocation;
xdg.configFile."gtk-3.0/settings.ini".text =
toGtk3Ini { Settings = gtkIni // cfg3.extraConfig; };
xdg.configFile."gtk-3.0/gtk.css" =
lib.mkIf (cfg3.extraCss != "") { text = cfg3.extraCss; };
xdg.configFile."gtk-3.0/bookmarks" = lib.mkIf (cfg3.bookmarks != [ ]) {
text = lib.concatMapStrings (l: l + "\n") cfg3.bookmarks;
};
xdg.configFile."gtk-4.0/settings.ini".text =
toGtk3Ini { Settings = gtkIni // cfg4.extraConfig; };
xdg.configFile."gtk-4.0/gtk.css" =
lib.mkIf (gtk4Css != "") { text = gtk4Css; };
dconf.settings."org/gnome/desktop/interface" = dconfIni;
});
dconf.settings."org/gnome/desktop/interface" = dconfIni;
}
);
}

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (pkgs.stdenv) isDarwin;
@ -10,16 +15,16 @@ let
(pkgs.writeTextDir "lib/mozilla/native-messaging-hosts/.keep" "")
];
thunderbirdNativeMessagingHostsPath = if isDarwin then
"Library/Mozilla/NativeMessagingHosts"
else
".mozilla/native-messaging-hosts";
thunderbirdNativeMessagingHostsPath =
if isDarwin then "Library/Mozilla/NativeMessagingHosts" else ".mozilla/native-messaging-hosts";
firefoxNativeMessagingHostsPath = if isDarwin then
"Library/Application Support/Mozilla/NativeMessagingHosts"
else
".mozilla/native-messaging-hosts";
in {
firefoxNativeMessagingHostsPath =
if isDarwin then
"Library/Application Support/Mozilla/NativeMessagingHosts"
else
".mozilla/native-messaging-hosts";
in
{
meta.maintainers = with lib.maintainers; [
booxter
rycee
@ -46,47 +51,45 @@ in {
};
};
config = lib.mkIf (cfg.firefoxNativeMessagingHosts != [ ]
|| cfg.thunderbirdNativeMessagingHosts != [ ]) {
home.file = if isDarwin then
let
firefoxNativeMessagingHostsJoined = pkgs.symlinkJoin {
name = "ff-native-messaging-hosts";
paths = defaultPaths ++ cfg.firefoxNativeMessagingHosts;
};
thunderbirdNativeMessagingHostsJoined = pkgs.symlinkJoin {
name = "th-native-messaging-hosts";
paths = defaultPaths ++ cfg.thunderbirdNativeMessagingHosts;
};
in {
"${thunderbirdNativeMessagingHostsPath}" =
lib.mkIf (cfg.thunderbirdNativeMessagingHosts != [ ]) {
source =
"${thunderbirdNativeMessagingHostsJoined}/lib/mozilla/native-messaging-hosts";
recursive = true;
};
config =
lib.mkIf (cfg.firefoxNativeMessagingHosts != [ ] || cfg.thunderbirdNativeMessagingHosts != [ ])
{
home.file =
if isDarwin then
let
firefoxNativeMessagingHostsJoined = pkgs.symlinkJoin {
name = "ff-native-messaging-hosts";
paths = defaultPaths ++ cfg.firefoxNativeMessagingHosts;
};
thunderbirdNativeMessagingHostsJoined = pkgs.symlinkJoin {
name = "th-native-messaging-hosts";
paths = defaultPaths ++ cfg.thunderbirdNativeMessagingHosts;
};
in
{
"${thunderbirdNativeMessagingHostsPath}" = lib.mkIf (cfg.thunderbirdNativeMessagingHosts != [ ]) {
source = "${thunderbirdNativeMessagingHostsJoined}/lib/mozilla/native-messaging-hosts";
recursive = true;
};
"${firefoxNativeMessagingHostsPath}" =
lib.mkIf (cfg.firefoxNativeMessagingHosts != [ ]) {
source =
"${firefoxNativeMessagingHostsJoined}/lib/mozilla/native-messaging-hosts";
recursive = true;
"${firefoxNativeMessagingHostsPath}" = lib.mkIf (cfg.firefoxNativeMessagingHosts != [ ]) {
source = "${firefoxNativeMessagingHostsJoined}/lib/mozilla/native-messaging-hosts";
recursive = true;
};
}
else
let
nativeMessagingHostsJoined = pkgs.symlinkJoin {
name = "mozilla-native-messaging-hosts";
# on Linux, the directory is shared between Firefox and Thunderbird; merge both into one
paths = defaultPaths ++ cfg.firefoxNativeMessagingHosts ++ cfg.thunderbirdNativeMessagingHosts;
};
in
{
"${firefoxNativeMessagingHostsPath}" = {
source = "${nativeMessagingHostsJoined}/lib/mozilla/native-messaging-hosts";
recursive = true;
};
};
}
else
let
nativeMessagingHostsJoined = pkgs.symlinkJoin {
name = "mozilla-native-messaging-hosts";
# on Linux, the directory is shared between Firefox and Thunderbird; merge both into one
paths = defaultPaths ++ cfg.firefoxNativeMessagingHosts
++ cfg.thunderbirdNativeMessagingHosts;
};
in {
"${firefoxNativeMessagingHostsPath}" = {
source =
"${nativeMessagingHostsJoined}/lib/mozilla/native-messaging-hosts";
recursive = true;
};
};
};
};
}

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkOption types;
@ -7,58 +12,64 @@ let
hostPlatform = pkgs.stdenv.hostPlatform;
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.
'';
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.";
};
};
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').
'';
config = {
id = lib.mkDefault (builtins.hashString "sha256" config.message);
};
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 = lib.mkDefault (builtins.hashString "sha256" config.message);
};
});
}
);
isNixFile = n: v: v == "regular" && lib.hasSuffix ".nix" n;
# builtins.attrNames return the values in alphabetical order
newsFiles =
builtins.attrNames (lib.filterAttrs isNixFile (builtins.readDir ./news));
newsEntries =
builtins.map (newsFile: import (./news + "/${newsFile}")) newsFiles;
in {
newsFiles = builtins.attrNames (lib.filterAttrs isNixFile (builtins.readDir ./news));
newsEntries = builtins.map (newsFile: import (./news + "/${newsFile}")) newsFiles;
in
{
meta.maintainers = [ lib.maintainers.rycee ];
options = {
news = {
display = mkOption {
type = types.enum [ "silent" "notify" "show" ];
type = types.enum [
"silent"
"notify"
"show"
];
default = "notify";
description = ''
How unread and relevant news should be presented when
@ -100,8 +111,9 @@ in {
};
config = {
news.json.output = pkgs.writeText "hm-news.json"
(builtins.toJSON { inherit (cfg) display entries; });
news.json.output = pkgs.writeText "hm-news.json" (
builtins.toJSON { inherit (cfg) display entries; }
);
# DO NOT define new entries here, instead use the `./create-news-entry.sh`
# script and create an individual news file inside `news` sub-directory.
@ -250,8 +262,7 @@ in {
{
time = "2021-09-23T17:04:48+00:00";
condition = hostPlatform.isLinux
&& config.services.screen-locker.enable;
condition = hostPlatform.isLinux && config.services.screen-locker.enable;
message = ''
'xautolock' is now optional in 'services.screen-locker', and the
'services.screen-locker' options have been reorganized for clarity.
@ -1699,9 +1710,12 @@ in {
{
time = "2024-06-26T07:07:17+00:00";
condition = with config.programs.yazi;
enable && (enableBashIntegration || enableZshIntegration
|| enableFishIntegration || enableNushellIntegration);
condition =
with config.programs.yazi;
enable
&& (
enableBashIntegration || enableZshIntegration || enableFishIntegration || enableNushellIntegration
);
message = ''
Yazi's shell integration wrappers have been renamed from 'ya' to 'yy'.
@ -1881,10 +1895,12 @@ in {
{
time = "2024-12-04T20:00:00+00:00";
condition = let
sCfg = config.programs.starship;
fCfg = config.programs.fish;
in sCfg.enable && sCfg.enableFishIntegration && fCfg.enable;
condition =
let
sCfg = config.programs.starship;
fCfg = config.programs.fish;
in
sCfg.enable && sCfg.enableFishIntegration && fCfg.enable;
message = ''
A new option 'programs.starship.enableInteractive' is available for
the Fish shell that only enables starship if the shell is interactive.
@ -1894,10 +1910,11 @@ in {
}
{
time = "2024-12-08T17:22:13+00:00";
condition = let
usingMbsync = lib.any (a: a.mbsync.enable)
(lib.attrValues config.accounts.email.accounts);
in usingMbsync;
condition =
let
usingMbsync = lib.any (a: a.mbsync.enable) (lib.attrValues config.accounts.email.accounts);
in
usingMbsync;
message = ''
isync/mbsync 1.5.0 has changed several things.

View file

@ -1,12 +1,38 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
boolToString concatStringsSep escape floatToString getVersion isBool
isConvertibleWithToString isDerivation isFloat isInt isList isString
literalExpression maintainers mapAttrsToList mkDefault mkEnableOption mkIf
mkMerge mkOption optionalString toPretty types versionAtLeast;
boolToString
concatStringsSep
escape
floatToString
getVersion
isBool
isConvertibleWithToString
isDerivation
isFloat
isInt
isList
isString
literalExpression
maintainers
mapAttrsToList
mkDefault
mkEnableOption
mkIf
mkMerge
mkOption
optionalString
toPretty
types
versionAtLeast
;
cfg = config.nix;
@ -16,29 +42,33 @@ let
nixPath = concatStringsSep ":" cfg.nixPath;
useXdg = config.nix.enable
&& (config.nix.settings.use-xdg-base-directories or false);
defexprDir = if useXdg then
"${config.xdg.stateHome}/nix/defexpr"
else
"${config.home.homeDirectory}/.nix-defexpr";
useXdg = config.nix.enable && (config.nix.settings.use-xdg-base-directories or false);
defexprDir =
if useXdg then
"${config.xdg.stateHome}/nix/defexpr"
else
"${config.home.homeDirectory}/.nix-defexpr";
# The deploy path for declarative channels. The directory name is prefixed
# with a number to make it easier for files in defexprDir to control the order
# they'll be read relative to each other.
channelPath = "${defexprDir}/50-home-manager";
channelsDrv = let
mkEntry = name: drv: {
inherit name;
path = toString drv;
};
in pkgs.linkFarm "channels" (lib.mapAttrsToList mkEntry cfg.channels);
channelsDrv =
let
mkEntry = name: drv: {
inherit name;
path = toString drv;
};
in
pkgs.linkFarm "channels" (lib.mapAttrsToList mkEntry cfg.channels);
nixConf = assert isNixAtLeast "2.2";
nixConf =
assert isNixAtLeast "2.2";
let
mkValueString = v:
mkValueString =
v:
if v == null then
""
else if isInt v then
@ -62,10 +92,10 @@ let
mkKeyValue = k: v: "${escape [ "=" ] k} = ${mkValueString v}";
mkKeyValuePairs = attrs:
concatStringsSep "\n" (mapAttrsToList mkKeyValue attrs);
mkKeyValuePairs = attrs: concatStringsSep "\n" (mapAttrsToList mkKeyValue attrs);
in pkgs.writeTextFile {
in
pkgs.writeTextFile {
name = "nix.conf";
text = ''
# WARNING: this file is generated from the nix.settings option in
@ -75,48 +105,58 @@ let
${cfg.extraOptions}
'';
checkPhase =
if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then ''
echo "Ignoring validation for cross-compilation"
'' else
if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then
''
echo "Ignoring validation for cross-compilation"
''
else
let
showCommand =
if isNixAtLeast "2.20pre" then "config show" else "show-config";
in ''
showCommand = if isNixAtLeast "2.20pre" then "config show" else "show-config";
in
''
echo "Validating generated nix.conf"
ln -s $out ./nix.conf
set -e
set +o pipefail
NIX_CONF_DIR=$PWD \
${cfg.package}/bin/nix ${showCommand} ${
optionalString (isNixAtLeast "2.3pre")
"--no-net --option experimental-features nix-command"
} \
${cfg.package}/bin/nix ${showCommand} ${optionalString (isNixAtLeast "2.3pre") "--no-net --option experimental-features nix-command"} \
|& sed -e 's/^warning:/error:/' \
| (! grep '${
if cfg.checkConfig then "^error:" else "^error: unknown setting"
}')
| (! grep '${if cfg.checkConfig then "^error:" else "^error: unknown setting"}')
set -o pipefail
'';
};
semanticConfType = with types;
semanticConfType =
with types;
let
confAtom = nullOr (oneOf [ bool int float str path package ]) // {
description =
"Nix config atom (null, bool, int, float, str, path or package)";
};
in attrsOf (either confAtom (listOf confAtom));
confAtom =
nullOr (oneOf [
bool
int
float
str
path
package
])
// {
description = "Nix config atom (null, bool, int, float, str, path or package)";
};
in
attrsOf (either confAtom (listOf confAtom));
jsonFormat = pkgs.formats.json { };
in {
in
{
options.nix = {
enable = mkEnableOption ''
the Nix configuration module
'' // {
default = true;
visible = false;
};
enable =
mkEnableOption ''
the Nix configuration module
''
// {
default = true;
visible = false;
};
package = mkOption {
type = types.nullOr types.package;
@ -169,60 +209,74 @@ in {
};
registry = mkOption {
type = types.attrsOf (types.submodule (let
inputAttrs = types.attrsOf
(types.oneOf [ types.str types.int types.bool types.package ]);
in { config, name, ... }: {
options = {
from = mkOption {
type = inputAttrs;
example = {
type = "indirect";
id = "nixpkgs";
type = types.attrsOf (
types.submodule (
let
inputAttrs = types.attrsOf (
types.oneOf [
types.str
types.int
types.bool
types.package
]
);
in
{ config, name, ... }:
{
options = {
from = mkOption {
type = inputAttrs;
example = {
type = "indirect";
id = "nixpkgs";
};
description = "The flake reference to be rewritten.";
};
to = mkOption {
type = inputAttrs;
example = {
type = "github";
owner = "my-org";
repo = "my-nixpkgs";
};
description = "The flake reference to which {option}`from>` is to be rewritten.";
};
flake = mkOption {
type = types.nullOr types.attrs;
default = null;
example = literalExpression "nixpkgs";
description = ''
The flake input to which {option}`from>` is to be rewritten.
'';
};
exact = mkOption {
type = types.bool;
default = true;
description = ''
Whether the {option}`from` reference needs to match exactly. If set,
a {option}`from` reference like `nixpkgs` does not
match with a reference like `nixpkgs/nixos-20.03`.
'';
};
};
description = "The flake reference to be rewritten.";
};
to = mkOption {
type = inputAttrs;
example = {
type = "github";
owner = "my-org";
repo = "my-nixpkgs";
config = {
from = mkDefault {
type = "indirect";
id = name;
};
to = mkIf (config.flake != null) (
{
type = "path";
path = config.flake.outPath;
}
// lib.filterAttrs (
n: v: n == "lastModified" || n == "rev" || n == "revCount" || n == "narHash"
) config.flake
);
};
description =
"The flake reference to which {option}`from>` is to be rewritten.";
};
flake = mkOption {
type = types.nullOr types.attrs;
default = null;
example = literalExpression "nixpkgs";
description = ''
The flake input to which {option}`from>` is to be rewritten.
'';
};
exact = mkOption {
type = types.bool;
default = true;
description = ''
Whether the {option}`from` reference needs to match exactly. If set,
a {option}`from` reference like `nixpkgs` does not
match with a reference like `nixpkgs/nixos-20.03`.
'';
};
};
config = {
from = mkDefault {
type = "indirect";
id = name;
};
to = mkIf (config.flake != null) ({
type = "path";
path = config.flake.outPath;
} // lib.filterAttrs (n: v:
n == "lastModified" || n == "rev" || n == "revCount" || n
== "narHash") config.flake);
};
}));
}
)
);
default = { };
description = ''
User level flake registry.
@ -290,22 +344,22 @@ in {
})
(mkIf (cfg.registry != { }) {
xdg.configFile."nix/registry.json".source =
jsonFormat.generate "registry.json" {
version = cfg.registryVersion;
flakes =
mapAttrsToList (n: v: { inherit (v) from to exact; }) cfg.registry;
};
xdg.configFile."nix/registry.json".source = jsonFormat.generate "registry.json" {
version = cfg.registryVersion;
flakes = mapAttrsToList (n: v: { inherit (v) from to exact; }) cfg.registry;
};
})
(mkIf (cfg.settings != { } || cfg.extraOptions != "") {
assertions = [{
assertion = cfg.package != null;
message = ''
A corresponding Nix package must be specified via `nix.package` for generating
nix.conf.
'';
}];
assertions = [
{
assertion = cfg.package != null;
message = ''
A corresponding Nix package must be specified via `nix.package` for generating
nix.conf.
'';
}
];
xdg.configFile."nix/nix.conf".source = nixConf;
})

View file

@ -1,13 +1,23 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.nixGL;
wrapperListMarkdown = with builtins;
foldl' (list: name:
list + ''
wrapperListMarkdown =
with builtins;
foldl' (
list: name:
list
+ ''
- ${name}
'') "" (attrNames config.lib.nixGL.wrappers);
in {
''
) "" (attrNames config.lib.nixGL.wrappers);
in
{
meta.maintainers = [ lib.maintainers.smona ];
options.nixGL = {
@ -93,7 +103,12 @@ in {
};
prime.installScript = lib.mkOption {
type = with lib.types; nullOr (enum [ "mesa" "nvidia" ]);
type =
with lib.types;
nullOr (enum [
"mesa"
"nvidia"
]);
default = null;
example = "mesa";
description = ''
@ -109,10 +124,12 @@ in {
};
installScripts = lib.mkOption {
type = with lib.types;
nullOr (listOf (enum (builtins.attrNames config.lib.nixGL.wrappers)));
type = with lib.types; nullOr (listOf (enum (builtins.attrNames config.lib.nixGL.wrappers)));
default = null;
example = [ "mesa" "mesaPrime" ];
example = [
"mesa"
"mesaPrime"
];
description = ''
For each wrapper `wrp` named in the provided list, a wrapper script
named `nixGLWrp` is installed into the environment. These scripts are
@ -137,168 +154,199 @@ in {
};
};
config = let
findWrapperPackage = packageAttr:
# NixGL has wrapper packages in different places depending on how you
# access it. We want HM configuration to be the same, regardless of how
# NixGL is imported.
#
# First, let's see if we have a flake.
if builtins.hasAttr pkgs.system cfg.packages then
cfg.packages.${pkgs.system}.${packageAttr}
else
# Next, let's see if we have a channel.
if builtins.hasAttr packageAttr cfg.packages then
cfg.packages.${packageAttr}
else
# Lastly, with channels, some wrappers are grouped under "auto".
if builtins.hasAttr "auto" cfg.packages then
cfg.packages.auto.${packageAttr}
else
throw "Incompatible NixGL package layout";
getWrapperExe = vendor:
let
glPackage = findWrapperPackage "nixGL${vendor}";
glExe = lib.getExe glPackage;
vulkanPackage = findWrapperPackage "nixVulkan${vendor}";
vulkanExe = if cfg.vulkan.enable then lib.getExe vulkanPackage else "";
in "${glExe} ${vulkanExe}";
mesaOffloadEnv = { "DRI_PRIME" = "${cfg.prime.card}"; };
nvOffloadEnv = {
"DRI_PRIME" = "${cfg.prime.card}";
"__NV_PRIME_RENDER_OFFLOAD" = "1";
"__GLX_VENDOR_LIBRARY_NAME" = "nvidia";
"__VK_LAYER_NV_optimus" = "NVIDIA_only";
} // (let provider = cfg.prime.nvidiaProvider;
in if !isNull provider then {
"__NV_PRIME_RENDER_OFFLOAD_PROVIDER" = "${provider}";
} else
{ });
makePackageWrapper = vendor: environment: pkg:
if builtins.isNull cfg.packages then
pkg
else
# Wrap the package's binaries with nixGL, while preserving the rest of
# the outputs and derivation attributes.
(pkg.overrideAttrs (old: {
name = "nixGL-${pkg.name}";
# Make sure this is false for the wrapper derivation, so nix doesn't expect
# a new debug output to be produced. We won't be producing any debug info
# for the original package.
separateDebugInfo = false;
nativeBuildInputs = old.nativeBuildInputs or [ ]
++ [ pkgs.makeWrapper ];
buildCommand = let
# We need an intermediate wrapper package because makeWrapper
# requires a single executable as the wrapper.
combinedWrapperPkg =
pkgs.writeShellScriptBin "nixGLCombinedWrapper-${vendor}" ''
exec ${getWrapperExe vendor} "$@"
'';
in ''
set -eo pipefail
${ # Heavily inspired by https://stackoverflow.com/a/68523368/6259505
lib.concatStringsSep "\n" (map (outputName: ''
echo "Copying output ${outputName}"
set -x
cp -rs --no-preserve=mode "${
pkg.${outputName}
}" "''$${outputName}"
set +x
'') (old.outputs or [ "out" ]))}
rm -rf $out/bin/*
shopt -s nullglob # Prevent loop from running if no files
for file in ${pkg.out}/bin/*; do
local prog="$(basename "$file")"
makeWrapper \
"${lib.getExe combinedWrapperPkg}" \
"$out/bin/$prog" \
--argv0 "$prog" \
--add-flags "$file" \
${
lib.concatStringsSep " " (lib.attrsets.mapAttrsToList
(var: val: "--set '${var}' '${val}'") environment)
}
done
# If .desktop files refer to the old package, replace the references
for dsk in "$out/share/applications"/*.desktop ; do
if ! grep -q "${pkg.out}" "$dsk"; then
continue
fi
src="$(readlink "$dsk")"
rm "$dsk"
sed "s|${pkg.out}|$out|g" "$src" > "$dsk"
done
shopt -u nullglob # Revert nullglob back to its normal default state
'';
})) // {
# When the nixGL-wrapped package is given to a HM module, the module
# might want to override the package arguments, but our wrapper
# wouldn't know what to do with them. So, we rewrite the override
# function to instead forward the arguments to the package's own
# override function.
override = args:
makePackageWrapper vendor environment (pkg.override args);
};
wrappers = {
mesa = makePackageWrapper "Intel" { };
mesaPrime = makePackageWrapper "Intel" mesaOffloadEnv;
nvidia = makePackageWrapper "Nvidia" { };
nvidiaPrime = makePackageWrapper "Nvidia" nvOffloadEnv;
};
in {
lib.nixGL.wrap = wrappers.${cfg.defaultWrapper};
lib.nixGL.wrapOffload = wrappers.${cfg.offloadWrapper};
lib.nixGL.wrappers = wrappers;
home.packages = let
wantsPrimeWrapper = (!isNull cfg.prime.installScript);
wantsWrapper = wrapper:
(!isNull cfg.packages) && (!isNull cfg.installScripts)
&& (builtins.elem wrapper cfg.installScripts);
envVarsAsScript = environment:
lib.concatStringsSep "\n"
(lib.attrsets.mapAttrsToList (var: val: "export ${var}=${val}")
environment);
in [
(lib.mkIf wantsPrimeWrapper (pkgs.writeShellScriptBin "prime-offload" ''
${if cfg.prime.installScript == "mesa" then
(envVarsAsScript mesaOffloadEnv)
config =
let
findWrapperPackage =
packageAttr:
# NixGL has wrapper packages in different places depending on how you
# access it. We want HM configuration to be the same, regardless of how
# NixGL is imported.
#
# First, let's see if we have a flake.
if builtins.hasAttr pkgs.system cfg.packages then
cfg.packages.${pkgs.system}.${packageAttr}
else
(envVarsAsScript nvOffloadEnv)}
exec "$@"
''))
# Next, let's see if we have a channel.
if builtins.hasAttr packageAttr cfg.packages then
cfg.packages.${packageAttr}
else
# Lastly, with channels, some wrappers are grouped under "auto".
if builtins.hasAttr "auto" cfg.packages then
cfg.packages.auto.${packageAttr}
else
throw "Incompatible NixGL package layout";
(lib.mkIf (wantsWrapper "mesa") (pkgs.writeShellScriptBin "nixGLMesa" ''
exec ${getWrapperExe "Intel"} "$@"
''))
getWrapperExe =
vendor:
let
glPackage = findWrapperPackage "nixGL${vendor}";
glExe = lib.getExe glPackage;
vulkanPackage = findWrapperPackage "nixVulkan${vendor}";
vulkanExe = if cfg.vulkan.enable then lib.getExe vulkanPackage else "";
in
"${glExe} ${vulkanExe}";
(lib.mkIf (wantsWrapper "mesaPrime")
(pkgs.writeShellScriptBin "nixGLMesaPrime" ''
${envVarsAsScript mesaOffloadEnv}
exec ${getWrapperExe "Intel"} "$@"
''))
mesaOffloadEnv = {
"DRI_PRIME" = "${cfg.prime.card}";
};
(lib.mkIf (wantsWrapper "nvidia")
(pkgs.writeShellScriptBin "nixGLNvidia" ''
exec ${getWrapperExe "Nvidia"} "$@"
''))
nvOffloadEnv =
{
"DRI_PRIME" = "${cfg.prime.card}";
"__NV_PRIME_RENDER_OFFLOAD" = "1";
"__GLX_VENDOR_LIBRARY_NAME" = "nvidia";
"__VK_LAYER_NV_optimus" = "NVIDIA_only";
}
// (
let
provider = cfg.prime.nvidiaProvider;
in
if !isNull provider then
{
"__NV_PRIME_RENDER_OFFLOAD_PROVIDER" = "${provider}";
}
else
{ }
);
(lib.mkIf (wantsWrapper "nvidia")
(pkgs.writeShellScriptBin "nixGLNvidiaPrime" ''
${envVarsAsScript nvOffloadEnv}
exec ${getWrapperExe "Nvidia"} "$@"
''))
];
};
makePackageWrapper =
vendor: environment: pkg:
if builtins.isNull cfg.packages then
pkg
else
# Wrap the package's binaries with nixGL, while preserving the rest of
# the outputs and derivation attributes.
(pkg.overrideAttrs (old: {
name = "nixGL-${pkg.name}";
# Make sure this is false for the wrapper derivation, so nix doesn't expect
# a new debug output to be produced. We won't be producing any debug info
# for the original package.
separateDebugInfo = false;
nativeBuildInputs = old.nativeBuildInputs or [ ] ++ [ pkgs.makeWrapper ];
buildCommand =
let
# We need an intermediate wrapper package because makeWrapper
# requires a single executable as the wrapper.
combinedWrapperPkg = pkgs.writeShellScriptBin "nixGLCombinedWrapper-${vendor}" ''
exec ${getWrapperExe vendor} "$@"
'';
in
''
set -eo pipefail
${
# Heavily inspired by https://stackoverflow.com/a/68523368/6259505
lib.concatStringsSep "\n" (
map (outputName: ''
echo "Copying output ${outputName}"
set -x
cp -rs --no-preserve=mode "${pkg.${outputName}}" "''$${outputName}"
set +x
'') (old.outputs or [ "out" ])
)
}
rm -rf $out/bin/*
shopt -s nullglob # Prevent loop from running if no files
for file in ${pkg.out}/bin/*; do
local prog="$(basename "$file")"
makeWrapper \
"${lib.getExe combinedWrapperPkg}" \
"$out/bin/$prog" \
--argv0 "$prog" \
--add-flags "$file" \
${lib.concatStringsSep " " (
lib.attrsets.mapAttrsToList (var: val: "--set '${var}' '${val}'") environment
)}
done
# If .desktop files refer to the old package, replace the references
for dsk in "$out/share/applications"/*.desktop ; do
if ! grep -q "${pkg.out}" "$dsk"; then
continue
fi
src="$(readlink "$dsk")"
rm "$dsk"
sed "s|${pkg.out}|$out|g" "$src" > "$dsk"
done
shopt -u nullglob # Revert nullglob back to its normal default state
'';
}))
// {
# When the nixGL-wrapped package is given to a HM module, the module
# might want to override the package arguments, but our wrapper
# wouldn't know what to do with them. So, we rewrite the override
# function to instead forward the arguments to the package's own
# override function.
override = args: makePackageWrapper vendor environment (pkg.override args);
};
wrappers = {
mesa = makePackageWrapper "Intel" { };
mesaPrime = makePackageWrapper "Intel" mesaOffloadEnv;
nvidia = makePackageWrapper "Nvidia" { };
nvidiaPrime = makePackageWrapper "Nvidia" nvOffloadEnv;
};
in
{
lib.nixGL.wrap = wrappers.${cfg.defaultWrapper};
lib.nixGL.wrapOffload = wrappers.${cfg.offloadWrapper};
lib.nixGL.wrappers = wrappers;
home.packages =
let
wantsPrimeWrapper = (!isNull cfg.prime.installScript);
wantsWrapper =
wrapper:
(!isNull cfg.packages)
&& (!isNull cfg.installScripts)
&& (builtins.elem wrapper cfg.installScripts);
envVarsAsScript =
environment:
lib.concatStringsSep "\n" (
lib.attrsets.mapAttrsToList (var: val: "export ${var}=${val}") environment
);
in
[
(lib.mkIf wantsPrimeWrapper (
pkgs.writeShellScriptBin "prime-offload" ''
${
if cfg.prime.installScript == "mesa" then
(envVarsAsScript mesaOffloadEnv)
else
(envVarsAsScript nvOffloadEnv)
}
exec "$@"
''
))
(lib.mkIf (wantsWrapper "mesa") (
pkgs.writeShellScriptBin "nixGLMesa" ''
exec ${getWrapperExe "Intel"} "$@"
''
))
(lib.mkIf (wantsWrapper "mesaPrime") (
pkgs.writeShellScriptBin "nixGLMesaPrime" ''
${envVarsAsScript mesaOffloadEnv}
exec ${getWrapperExe "Intel"} "$@"
''
))
(lib.mkIf (wantsWrapper "nvidia") (
pkgs.writeShellScriptBin "nixGLNvidia" ''
exec ${getWrapperExe "Nvidia"} "$@"
''
))
(lib.mkIf (wantsWrapper "nvidia") (
pkgs.writeShellScriptBin "nixGLNvidiaPrime" ''
${envVarsAsScript nvOffloadEnv}
exec ${getWrapperExe "Nvidia"} "$@"
''
))
];
};
}

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
@ -11,16 +16,22 @@ let
optCall = f: x: if builtins.isFunction f then f x else f;
# Copied from nixpkgs.nix.
mergeConfig = lhs_: rhs_:
mergeConfig =
lhs_: rhs_:
let
lhs = optCall lhs_ { inherit pkgs; };
rhs = optCall rhs_ { inherit pkgs; };
in lhs // rhs // lib.optionalAttrs (lhs ? packageOverrides) {
packageOverrides = pkgs:
optCall lhs.packageOverrides pkgs
// optCall (lib.attrByPath [ "packageOverrides" ] { } rhs) pkgs;
} // lib.optionalAttrs (lhs ? perlPackageOverrides) {
perlPackageOverrides = pkgs:
in
lhs
// rhs
// lib.optionalAttrs (lhs ? packageOverrides) {
packageOverrides =
pkgs:
optCall lhs.packageOverrides pkgs // optCall (lib.attrByPath [ "packageOverrides" ] { } rhs) pkgs;
}
// lib.optionalAttrs (lhs ? perlPackageOverrides) {
perlPackageOverrides =
pkgs:
optCall lhs.perlPackageOverrides pkgs
// optCall (lib.attrByPath [ "perlPackageOverrides" ] { } rhs) pkgs;
};
@ -29,9 +40,12 @@ let
configType = lib.mkOptionType {
name = "nixpkgs-config";
description = "nixpkgs config";
check = x:
let traceXIfNot = c: if c x then true else lib.traceSeqN 1 x false;
in traceXIfNot isConfig;
check =
x:
let
traceXIfNot = c: if c x then true else lib.traceSeqN 1 x false;
in
traceXIfNot isConfig;
merge = args: lib.fold (def: mergeConfig def.value) { };
};
@ -43,7 +57,8 @@ let
merge = lib.mergeOneOption;
};
in {
in
{
meta.maintainers = with lib.maintainers; [ thiagokokada ];
options.nixpkgs = {

View file

@ -1,6 +1,12 @@
# Adapted from Nixpkgs.
{ config, lib, pkgs, pkgsPath, ... }:
{
config,
lib,
pkgs,
pkgsPath,
...
}:
let
@ -8,16 +14,22 @@ let
optCall = f: x: if builtins.isFunction f then f x else f;
mergeConfig = lhs_: rhs_:
mergeConfig =
lhs_: rhs_:
let
lhs = optCall lhs_ { inherit pkgs; };
rhs = optCall rhs_ { inherit pkgs; };
in lhs // rhs // lib.optionalAttrs (lhs ? packageOverrides) {
packageOverrides = pkgs:
optCall lhs.packageOverrides pkgs
// optCall (lib.attrByPath [ "packageOverrides" ] { } rhs) pkgs;
} // lib.optionalAttrs (lhs ? perlPackageOverrides) {
perlPackageOverrides = pkgs:
in
lhs
// rhs
// lib.optionalAttrs (lhs ? packageOverrides) {
packageOverrides =
pkgs:
optCall lhs.packageOverrides pkgs // optCall (lib.attrByPath [ "packageOverrides" ] { } rhs) pkgs;
}
// lib.optionalAttrs (lhs ? perlPackageOverrides) {
perlPackageOverrides =
pkgs:
optCall lhs.perlPackageOverrides pkgs
// optCall (lib.attrByPath [ "perlPackageOverrides" ] { } rhs) pkgs;
};
@ -25,9 +37,12 @@ let
configType = lib.mkOptionType {
name = "nixpkgs-config";
description = "nixpkgs config";
check = x:
let traceXIfNot = c: if c x then true else lib.traceSeqN 1 x false;
in traceXIfNot isConfig;
check =
x:
let
traceXIfNot = c: if c x then true else lib.traceSeqN 1 x false;
in
traceXIfNot isConfig;
merge = args: lib.fold (def: mergeConfig def.value) { };
};
@ -40,11 +55,14 @@ let
_pkgs = import pkgsPath (lib.filterAttrs (n: v: v != null) config.nixpkgs);
in {
in
{
options.nixpkgs = {
config = lib.mkOption {
default = null;
example = { allowBroken = true; };
example = {
allowBroken = true;
};
type = lib.types.nullOr configType;
description = ''
The configuration of the Nix Packages collection. (For
@ -123,10 +141,7 @@ in {
# `_pkgs`, see https://github.com/nix-community/home-manager/pull/993
pkgs = lib.mkOverride lib.modules.defaultOverridePriority _pkgs;
pkgs_i686 =
if _pkgs.stdenv.isLinux && _pkgs.stdenv.hostPlatform.isx86 then
_pkgs.pkgsi686Linux
else
{ };
if _pkgs.stdenv.isLinux && _pkgs.stdenv.hostPlatform.isx86 then _pkgs.pkgsi686Linux else { };
};
};
}

View file

@ -1,18 +1,25 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.xsession.numlock;
in {
in
{
meta.maintainers = [ lib.maintainers.evanjs ];
options = { xsession.numlock.enable = lib.mkEnableOption "Num Lock"; };
options = {
xsession.numlock.enable = lib.mkEnableOption "Num Lock";
};
config = lib.mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "xsession.numlock" pkgs
lib.platforms.linux)
(lib.hm.assertions.assertPlatform "xsession.numlock" pkgs lib.platforms.linux)
];
systemd.user.services.numlockx = {
@ -28,7 +35,9 @@ in {
ExecStart = "${pkgs.numlockx}/bin/numlockx";
};
Install = { WantedBy = [ "graphical-session.target" ]; };
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
};
}

View file

@ -4,14 +4,20 @@ let
cfg = config.pam;
in {
meta.maintainers = with lib.maintainers; [ rycee veehaitch ];
in
{
meta.maintainers = with lib.maintainers; [
rycee
veehaitch
];
options = {
pam.sessionVariables = lib.mkOption {
default = { };
type = lib.types.attrs;
example = { EDITOR = "vim"; };
example = {
EDITOR = "vim";
};
description = ''
Environment variables that will be set for the PAM session.
The variable values must be as described in
@ -24,13 +30,15 @@ in {
pam.yubico.authorizedYubiKeys = {
ids = lib.mkOption {
type = with lib.types;
type =
with lib.types;
let
yubiKeyId = addCheck str (s: lib.stringLength s == 12) // {
name = "yubiKeyId";
description = "string of length 12";
};
in listOf yubiKeyId;
in
listOf yubiKeyId;
default = [ ];
description = ''
List of authorized YubiKey token IDs. Refer to
@ -52,15 +60,17 @@ in {
config = lib.mkMerge [
(lib.mkIf (cfg.sessionVariables != { }) {
home.file.".pam_environment".text = lib.concatStringsSep "\n"
(lib.mapAttrsToList (n: v: ''${n} OVERRIDE="${toString v}"'')
cfg.sessionVariables) + "\n";
home.file.".pam_environment".text =
lib.concatStringsSep "\n" (
lib.mapAttrsToList (n: v: ''${n} OVERRIDE="${toString v}"'') cfg.sessionVariables
)
+ "\n";
})
(lib.mkIf (cfg.yubico.authorizedYubiKeys.ids != [ ]) {
home.file.${cfg.yubico.authorizedYubiKeys.path}.text =
lib.concatStringsSep ":"
([ config.home.username ] ++ cfg.yubico.authorizedYubiKeys.ids);
home.file.${cfg.yubico.authorizedYubiKeys.path}.text = lib.concatStringsSep ":" (
[ config.home.username ] ++ cfg.yubico.authorizedYubiKeys.ids
);
})
];
}

View file

@ -1,13 +1,27 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.qt;
# Map platform names to their packages.
platformPackages = with pkgs; {
gnome = [ qgnomeplatform qgnomeplatform-qt6 ];
adwaita = [ qadwaitadecorations qadwaitadecorations-qt6 ];
gtk = [ libsForQt5.qtstyleplugins qt6Packages.qt6gtk2 ];
gnome = [
qgnomeplatform
qgnomeplatform-qt6
];
adwaita = [
qadwaitadecorations
qadwaitadecorations-qt6
];
gtk = [
libsForQt5.qtstyleplugins
qt6Packages.qt6gtk2
];
kde = [
libsForQt5.kio
libsForQt5.plasma-integration
@ -18,8 +32,14 @@ let
kdePackages.plasma-integration
kdePackages.systemsettings
];
lxqt = [ lxqt.lxqt-qtplugin lxqt.lxqt-config ];
qtct = [ libsForQt5.qt5ct qt6Packages.qt6ct ];
lxqt = [
lxqt.lxqt-qtplugin
lxqt.lxqt-config
];
qtct = [
libsForQt5.qt5ct
qt6Packages.qt6ct
];
};
# Maps style names to their QT_QPA_PLATFORMTHEME, if necessary.
@ -34,122 +54,181 @@ let
bb10bright = libsForQt5.qtstyleplugins;
bb10dark = libsForQt5.qtstyleplugins;
cleanlooks = libsForQt5.qtstyleplugins;
gtk2 = [ libsForQt5.qtstyleplugins qt6Packages.qt6gtk2 ];
gtk2 = [
libsForQt5.qtstyleplugins
qt6Packages.qt6gtk2
];
motif = libsForQt5.qtstyleplugins;
cde = libsForQt5.qtstyleplugins;
plastique = libsForQt5.qtstyleplugins;
adwaita = [ adwaita-qt adwaita-qt6 ];
adwaita-dark = [ adwaita-qt adwaita-qt6 ];
adwaita-highcontrast = [ adwaita-qt adwaita-qt6 ];
adwaita-highcontrastinverse = [ adwaita-qt adwaita-qt6 ];
adwaita = [
adwaita-qt
adwaita-qt6
];
adwaita-dark = [
adwaita-qt
adwaita-qt6
];
adwaita-highcontrast = [
adwaita-qt
adwaita-qt6
];
adwaita-highcontrastinverse = [
adwaita-qt
adwaita-qt6
];
breeze = libsForQt5.breeze-qt5;
kvantum =
[ libsForQt5.qtstyleplugin-kvantum qt6Packages.qtstyleplugin-kvantum ];
kvantum = [
libsForQt5.qtstyleplugin-kvantum
qt6Packages.qtstyleplugin-kvantum
];
};
in {
meta.maintainers = with lib.maintainers; [ rycee thiagokokada ];
in
{
meta.maintainers = with lib.maintainers; [
rycee
thiagokokada
];
imports = [
(lib.mkChangedOptionModule [ "qt" "useGtkTheme" ] [ "qt" "platformTheme" ]
(config:
if lib.getAttrFromPath [ "qt" "useGtkTheme" ] config then
"gtk"
else
null))
(lib.mkChangedOptionModule [ "qt" "useGtkTheme" ] [ "qt" "platformTheme" ] (
config: if lib.getAttrFromPath [ "qt" "useGtkTheme" ] config then "gtk" else null
))
];
options = {
qt = {
enable = lib.mkEnableOption "Qt 5 and 6 configuration";
platformTheme = let
newOption = {
name = lib.mkOption {
type = with lib.types; nullOr str;
default = null;
example = "adwaita";
relatedPackages = [
"qgnomeplatform"
"qgnomeplatform-qt6"
"qadwaitadecorations"
"qadwaitadecorations-qt6"
[ "libsForQt5" "plasma-integration" ]
[ "libsForQt5" "qt5ct" ]
[ "libsForQt5" "qtstyleplugins" ]
[ "libsForQt5" "systemsettings" ]
[ "kdePackages" "plasma-integration" ]
[ "kdePackages" "systemsettings" ]
[ "lxqt" "lxqt-config" ]
[ "lxqt" "lxqt-qtplugin" ]
[ "qt6Packages" "qt6ct" ]
[ "qt6Packages" "qt6gtk2" ]
];
description = ''
Platform theme to use for Qt applications.
platformTheme =
let
newOption = {
name = lib.mkOption {
type = with lib.types; nullOr str;
default = null;
example = "adwaita";
relatedPackages = [
"qgnomeplatform"
"qgnomeplatform-qt6"
"qadwaitadecorations"
"qadwaitadecorations-qt6"
[
"libsForQt5"
"plasma-integration"
]
[
"libsForQt5"
"qt5ct"
]
[
"libsForQt5"
"qtstyleplugins"
]
[
"libsForQt5"
"systemsettings"
]
[
"kdePackages"
"plasma-integration"
]
[
"kdePackages"
"systemsettings"
]
[
"lxqt"
"lxqt-config"
]
[
"lxqt"
"lxqt-qtplugin"
]
[
"qt6Packages"
"qt6ct"
]
[
"qt6Packages"
"qt6gtk2"
]
];
description = ''
Platform theme to use for Qt applications.
Some examples are
Some examples are
`gtk`
: Use GTK theme with
[`qtstyleplugins`](https://github.com/qt/qtstyleplugins)
`gtk`
: Use GTK theme with
[`qtstyleplugins`](https://github.com/qt/qtstyleplugins)
`gtk3`
: Use [GTK3 integration](https://github.com/qt/qtbase/tree/dev/src/plugins/platformthemes/gtk3)
for file picker dialogs, font and theme configuration
`gtk3`
: Use [GTK3 integration](https://github.com/qt/qtbase/tree/dev/src/plugins/platformthemes/gtk3)
for file picker dialogs, font and theme configuration
`adwaita`
: Use Adwaita theme with
[`qadwaitadecorations`](https://github.com/FedoraQt/QAdwaitaDecorations)
`adwaita`
: Use Adwaita theme with
[`qadwaitadecorations`](https://github.com/FedoraQt/QAdwaitaDecorations)
`gnome` (deprecated)
: Use GNOME theme with
[`qgnomeplatform`](https://github.com/FedoraQt/QGnomePlatform).
Is no longer maintained so prefer `adwaita`.
`gnome` (deprecated)
: Use GNOME theme with
[`qgnomeplatform`](https://github.com/FedoraQt/QGnomePlatform).
Is no longer maintained so prefer `adwaita`.
`lxqt`
: Use LXQt theme style set using the
[`lxqt-config-appearance`](https://github.com/lxqt/lxqt-config)
application
`lxqt`
: Use LXQt theme style set using the
[`lxqt-config-appearance`](https://github.com/lxqt/lxqt-config)
application
`qtct`
: Use Qt style set using
[`qt5ct`](https://github.com/desktop-app/qt5ct)
and [`qt6ct`](https://github.com/trialuser02/qt6ct)
applications
`qtct`
: Use Qt style set using
[`qt5ct`](https://github.com/desktop-app/qt5ct)
and [`qt6ct`](https://github.com/trialuser02/qt6ct)
applications
`kde`
: Use Qt settings from Plasma 5
`kde`
: Use Qt settings from Plasma 5
`kde6`
: Use Qt settings from Plasma 6
'';
};
package = lib.mkOption {
type = with lib.types; nullOr (either package (listOf package));
default = null;
example =
lib.literalExpression "[pkgs.adwaita-qt pkgs.adwaita-qt6]";
description = ''
Theme package to be used in Qt5/Qt6 applications.
Auto-detected from {option}`qt.platformTheme.name` if possible.
See its documentation for available options.
'';
`kde6`
: Use Qt settings from Plasma 6
'';
};
package = lib.mkOption {
type = with lib.types; nullOr (either package (listOf package));
default = null;
example = lib.literalExpression "[pkgs.adwaita-qt pkgs.adwaita-qt6]";
description = ''
Theme package to be used in Qt5/Qt6 applications.
Auto-detected from {option}`qt.platformTheme.name` if possible.
See its documentation for available options.
'';
};
};
in
lib.mkOption {
type =
with lib.types;
nullOr (
either (enum [
"gtk"
"gtk3"
"gnome"
"adwaita"
"lxqt"
"qtct"
"kde"
"kde6"
]) (lib.types.submodule { options = newOption; })
);
default = null;
description = ''
Deprecated. Use {option}`qt.platformTheme.name` instead.
'';
};
in lib.mkOption {
type = with lib.types;
nullOr (either
(enum [ "gtk" "gtk3" "gnome" "adwaita" "lxqt" "qtct" "kde" "kde6" ])
(lib.types.submodule { options = newOption; }));
default = null;
description = ''
Deprecated. Use {option}`qt.platformTheme.name` instead.
'';
};
style = {
name = lib.mkOption {
type = with lib.types; nullOr str;
@ -158,11 +237,26 @@ in {
relatedPackages = [
"adwaita-qt"
"adwaita-qt6"
[ "libsForQt5" "breeze-qt5" ]
[ "libsForQt5" "qtstyleplugin-kvantum" ]
[ "libsForQt5" "qtstyleplugins" ]
[ "qt6Packages" "qt6gtk2" ]
[ "qt6Packages" "qtstyleplugin-kvantum" ]
[
"libsForQt5"
"breeze-qt5"
]
[
"libsForQt5"
"qtstyleplugin-kvantum"
]
[
"libsForQt5"
"qtstyleplugins"
]
[
"qt6Packages"
"qt6gtk2"
]
[
"qt6Packages"
"qtstyleplugin-kvantum"
]
];
description = ''
Style to use for Qt5/Qt6 applications. Case-insensitive.
@ -201,80 +295,103 @@ in {
};
};
config = let
platformTheme = if (builtins.isString cfg.platformTheme) then {
option = "qt.platformTheme";
name = cfg.platformTheme;
package = null;
} else if cfg.platformTheme == null then {
option = null;
name = null;
package = null;
} else {
option = "qt.platformTheme.name";
name = cfg.platformTheme.name;
package = cfg.platformTheme.package;
config =
let
platformTheme =
if (builtins.isString cfg.platformTheme) then
{
option = "qt.platformTheme";
name = cfg.platformTheme;
package = null;
}
else if cfg.platformTheme == null then
{
option = null;
name = null;
package = null;
}
else
{
option = "qt.platformTheme.name";
name = cfg.platformTheme.name;
package = cfg.platformTheme.package;
};
# Necessary because home.sessionVariables doesn't support mkIf
envVars = lib.filterAttrs (n: v: v != null) {
QT_QPA_PLATFORMTHEME =
if (platformTheme.name != null) then
styleNames.${platformTheme.name} or platformTheme.name
else
null;
QT_STYLE_OVERRIDE = cfg.style.name;
};
envVarsExtra =
let
inherit (config.home) profileDirectory;
qtVersions = with pkgs; [
qt5
qt6
];
makeQtPath = prefix: (map (qt: "${profileDirectory}/${qt.qtbase.${prefix}}") qtVersions);
in
{
QT_PLUGIN_PATH = makeQtPath "qtPluginPrefix";
QML2_IMPORT_PATH = makeQtPath "qtQmlPrefix";
};
in
lib.mkIf cfg.enable {
assertions = [
{
assertion = platformTheme.name == "gnome" -> cfg.style.name != null && cfg.style.package != null;
message = ''
`qt.platformTheme.name` "gnome" must have `qt.style` set to a theme that
supports both Qt and Gtk, for example "adwaita", "adwaita-dark", or "breeze".
'';
}
];
warnings =
(lib.lists.optional (
platformTheme.option == "qt.platformTheme"
) "The option `qt.platformTheme` has been renamed to `qt.platformTheme.name`.")
++ (lib.lists.optional (
platformTheme.name == "gnome" && platformTheme.package == null
) "The value `gnome` for option `${platformTheme.option}` is deprecated. Use `adwaita` instead.");
qt.style.package = lib.mkIf (cfg.style.name != null) (
lib.mkDefault (stylePackages.${lib.toLower cfg.style.name} or null)
);
home = {
sessionVariables = envVars;
sessionSearchVariables = envVarsExtra;
};
# Apply theming also to apps started by systemd.
systemd.user.sessionVariables = envVars // {
QT_PLUGIN_PATH = lib.concatStringsSep ":" envVarsExtra.QT_PLUGIN_PATH;
QML2_IMPORT_PATH = lib.concatStringsSep ":" envVarsExtra.QML2_IMPORT_PATH;
};
home.packages =
(lib.findFirst (x: x != [ ])
[ ]
[
(lib.optionals (platformTheme.package != null) (lib.toList platformTheme.package))
(lib.optionals (platformTheme.name != null) platformPackages.${platformTheme.name} or [ ])
]
)
++ (lib.optionals (cfg.style.package != null) (lib.toList cfg.style.package));
xsession.importedVariables =
[
"QT_PLUGIN_PATH"
"QML2_IMPORT_PATH"
]
++ lib.optionals (platformTheme.name != null) [ "QT_QPA_PLATFORMTHEME" ]
++ lib.optionals (cfg.style.name != null) [ "QT_STYLE_OVERRIDE" ];
};
# Necessary because home.sessionVariables doesn't support mkIf
envVars = lib.filterAttrs (n: v: v != null) {
QT_QPA_PLATFORMTHEME = if (platformTheme.name != null) then
styleNames.${platformTheme.name} or platformTheme.name
else
null;
QT_STYLE_OVERRIDE = cfg.style.name;
};
envVarsExtra = let
inherit (config.home) profileDirectory;
qtVersions = with pkgs; [ qt5 qt6 ];
makeQtPath = prefix:
(map (qt: "${profileDirectory}/${qt.qtbase.${prefix}}") qtVersions);
in {
QT_PLUGIN_PATH = makeQtPath "qtPluginPrefix";
QML2_IMPORT_PATH = makeQtPath "qtQmlPrefix";
};
in lib.mkIf cfg.enable {
assertions = [{
assertion = platformTheme.name == "gnome" -> cfg.style.name != null
&& cfg.style.package != null;
message = ''
`qt.platformTheme.name` "gnome" must have `qt.style` set to a theme that
supports both Qt and Gtk, for example "adwaita", "adwaita-dark", or "breeze".
'';
}];
warnings = (lib.lists.optional (platformTheme.option == "qt.platformTheme")
"The option `qt.platformTheme` has been renamed to `qt.platformTheme.name`.")
++ (lib.lists.optional
(platformTheme.name == "gnome" && platformTheme.package == null)
"The value `gnome` for option `${platformTheme.option}` is deprecated. Use `adwaita` instead.");
qt.style.package = lib.mkIf (cfg.style.name != null)
(lib.mkDefault (stylePackages.${lib.toLower cfg.style.name} or null));
home = {
sessionVariables = envVars;
sessionSearchVariables = envVarsExtra;
};
# Apply theming also to apps started by systemd.
systemd.user.sessionVariables = envVars // {
QT_PLUGIN_PATH = lib.concatStringsSep ":" envVarsExtra.QT_PLUGIN_PATH;
QML2_IMPORT_PATH = lib.concatStringsSep ":" envVarsExtra.QML2_IMPORT_PATH;
};
home.packages = (lib.findFirst (x: x != [ ]) [ ] [
(lib.optionals (platformTheme.package != null)
(lib.toList platformTheme.package))
(lib.optionals (platformTheme.name != null)
platformPackages.${platformTheme.name} or [ ])
]) ++ (lib.optionals (cfg.style.package != null)
(lib.toList cfg.style.package));
xsession.importedVariables = [ "QT_PLUGIN_PATH" "QML2_IMPORT_PATH" ]
++ lib.optionals (platformTheme.name != null) [ "QT_QPA_PLATFORMTHEME" ]
++ lib.optionals (cfg.style.name != null) [ "QT_STYLE_OVERRIDE" ];
};
}

View file

@ -1,17 +1,33 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
let
cfg = config.qt.kde.settings;
in {
in
{
options.qt.kde.settings = lib.mkOption {
type = with lib.types;
type =
with lib.types;
let
valueType =
nullOr (oneOf [ bool int float str path (attrsOf valueType) ]) // {
nullOr (oneOf [
bool
int
float
str
path
(attrsOf valueType)
])
// {
description = "KDE option value";
};
in attrsOf valueType;
in
attrsOf valueType;
default = { };
example = {
powermanagementprofilesrc.AC.HandleButtonEvents.lidAction = 32;
@ -38,28 +54,32 @@ in {
config = lib.mkIf (cfg != { }) {
home.activation.kconfig = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
${let
inherit (config.xdg) configHome;
toValue = v:
let t = builtins.typeOf v;
in if v == null then
"--delete"
else if t == "bool" then
"--type bool ${builtins.toJSON v}"
else
lib.escapeShellArg (toString v);
toLine = file: path: value:
if builtins.isAttrs value then
lib.mapAttrsToList
(group: value: toLine file (path ++ [ group ]) value) value
else
"run ${pkgs.kdePackages.kconfig}/bin/kwriteconfig6 --file '${configHome}/${file}' ${
lib.concatMapStringsSep " " (x: "--group ${x}")
(lib.lists.init path)
} --key '${lib.lists.last path}' ${toValue value}";
lines = lib.flatten
(lib.mapAttrsToList (file: attrs: toLine file [ ] attrs) cfg);
in builtins.concatStringsSep "\n" lines}
${
let
inherit (config.xdg) configHome;
toValue =
v:
let
t = builtins.typeOf v;
in
if v == null then
"--delete"
else if t == "bool" then
"--type bool ${builtins.toJSON v}"
else
lib.escapeShellArg (toString v);
toLine =
file: path: value:
if builtins.isAttrs value then
lib.mapAttrsToList (group: value: toLine file (path ++ [ group ]) value) value
else
"run ${pkgs.kdePackages.kconfig}/bin/kwriteconfig6 --file '${configHome}/${file}' ${
lib.concatMapStringsSep " " (x: "--group ${x}") (lib.lists.init path)
} --key '${lib.lists.last path}' ${toValue value}";
lines = lib.flatten (lib.mapAttrsToList (file: attrs: toLine file [ ] attrs) cfg);
in
builtins.concatStringsSep "\n" lines
}
# TODO: some way to only call the dbus calls needed
run ${pkgs.kdePackages.qttools}/bin/qdbus org.kde.KWin /KWin reconfigure || echo "KWin reconfigure failed"

View file

@ -1,36 +1,47 @@
{ config, name, extendModules, lib, ... }:
{
config,
name,
extendModules,
lib,
...
}:
{
imports =
[ (lib.mkRenamedOptionModule [ "specialization" ] [ "specialisation" ]) ];
imports = [ (lib.mkRenamedOptionModule [ "specialization" ] [ "specialisation" ]) ];
options.specialisation = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule {
options = {
configuration = lib.mkOption {
type = let
extended = extendModules {
modules = [{
# Prevent infinite recursion
specialisation = lib.mkOverride 0 { };
type = lib.types.attrsOf (
lib.types.submodule {
options = {
configuration = lib.mkOption {
type =
let
extended = extendModules {
modules = [
{
# Prevent infinite recursion
specialisation = lib.mkOverride 0 { };
# If used inside the NixOS/nix-darwin module, we get conflicting definitions
# of `name` inside the specialisation: one is the user name coming from the
# NixOS module definition and the other is `configuration`, the name of this
# option. Thus we need to explicitly wire the former into the module arguments.
# See discussion at https://github.com/nix-community/home-manager/issues/3716
_module.args.name = lib.mkForce name;
}];
};
in extended.type;
default = { };
visible = "shallow";
description = ''
Arbitrary Home Manager configuration settings.
'';
# If used inside the NixOS/nix-darwin module, we get conflicting definitions
# of `name` inside the specialisation: one is the user name coming from the
# NixOS module definition and the other is `configuration`, the name of this
# option. Thus we need to explicitly wire the former into the module arguments.
# See discussion at https://github.com/nix-community/home-manager/issues/3716
_module.args.name = lib.mkForce name;
}
];
};
in
extended.type;
default = { };
visible = "shallow";
description = ''
Arbitrary Home Manager configuration settings.
'';
};
};
};
});
}
);
default = { };
description = ''
A set of named specialized configurations. These can be used to extend
@ -71,18 +82,21 @@
config = lib.mkIf (config.specialisation != { }) {
assertions = map (n: {
assertion = !lib.hasInfix "/" n;
message =
"<name> in specialisation.<name> cannot contain a forward slash.";
message = "<name> in specialisation.<name> cannot contain a forward slash.";
}) (lib.attrNames config.specialisation);
home.extraBuilderCommands = let
link = n: v:
let pkg = v.configuration.home.activationPackage;
in "ln -s ${pkg} $out/specialisation/${lib.escapeShellArg n}";
in ''
mkdir $out/specialisation
${lib.concatStringsSep "\n"
(lib.mapAttrsToList link config.specialisation)}
'';
home.extraBuilderCommands =
let
link =
n: v:
let
pkg = v.configuration.home.activationPackage;
in
"ln -s ${pkg} $out/specialisation/${lib.escapeShellArg n}";
in
''
mkdir $out/specialisation
${lib.concatStringsSep "\n" (lib.mapAttrsToList link config.specialisation)}
'';
};
}

View file

@ -1,10 +1,16 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.systemd.user.tmpfiles;
in {
in
{
meta.maintainers = [ lib.maintainers.dawidsowa ];
options.systemd.user.tmpfiles.rules = lib.mkOption {
@ -21,8 +27,7 @@ in {
config = lib.mkIf (cfg.rules != [ ]) {
assertions = [
(lib.hm.assertions.assertPlatform "systemd.user.tmpfiles" pkgs
lib.platforms.linux)
(lib.hm.assertions.assertPlatform "systemd.user.tmpfiles" pkgs lib.platforms.linux)
];
xdg.configFile = {

View file

@ -4,7 +4,8 @@ let
inherit (lib) mkIf mkOption types;
in {
in
{
options.uninstall = mkOption {
type = types.bool;
default = false;
@ -26,25 +27,24 @@ in {
manual.manpages.enable = lib.mkForce false;
news.display = lib.mkForce "silent";
home.activation.uninstall =
lib.hm.dag.entryAfter [ "installPackages" "linkGeneration" ] ''
nixProfileRemove home-manager-path
home.activation.uninstall = lib.hm.dag.entryAfter [ "installPackages" "linkGeneration" ] ''
nixProfileRemove home-manager-path
if [[ -e $hmDataPath ]]; then
run rm $VERBOSE_ARG -r "$hmDataPath"
fi
if [[ -e $hmDataPath ]]; then
run rm $VERBOSE_ARG -r "$hmDataPath"
fi
if [[ -e $hmStatePath ]]; then
run rm $VERBOSE_ARG -r "$hmStatePath"
fi
if [[ -e $hmStatePath ]]; then
run rm $VERBOSE_ARG -r "$hmStatePath"
fi
if [[ -e $genProfilePath ]]; then
run rm $VERBOSE_ARG "$genProfilePath"*
fi
if [[ -e $genProfilePath ]]; then
run rm $VERBOSE_ARG "$genProfilePath"*
fi
if [[ -e $legacyGenGcPath ]]; then
run rm $VERBOSE_ARG "$legacyGenGcPath"
fi
'';
if [[ -e $legacyGenGcPath ]]; then
run rm $VERBOSE_ARG "$legacyGenGcPath"
fi
'';
};
}

View file

@ -5,7 +5,8 @@ let
releaseInfo = lib.importJSON ../../release.json;
in {
in
{
options = {
home.stateVersion = lib.mkOption {
type = types.enum [
@ -44,11 +45,12 @@ in {
internal = true;
readOnly = true;
type = types.str;
default = let
inherit (config.home.version) release revision;
suffix = lib.optionalString (revision != null)
"+${lib.substring 0 8 revision}";
in "${release}${suffix}";
default =
let
inherit (config.home.version) release revision;
suffix = lib.optionalString (revision != null) "+${lib.substring 0 8 revision}";
in
"${release}${suffix}";
example = "22.11+213a0629";
description = "The full Home Manager version.";
};
@ -76,11 +78,11 @@ in {
revision = lib.mkOption {
internal = true;
type = types.nullOr types.str;
default = let gitRepo = "${toString ./../..}/.git";
in if lib.pathIsGitRepo gitRepo then
lib.commitIdFromGitRepo gitRepo
else
null;
default =
let
gitRepo = "${toString ./../..}/.git";
in
if lib.pathIsGitRepo gitRepo then lib.commitIdFromGitRepo gitRepo else null;
description = ''
The Git revision from which this Home Manager configuration was built.
'';

View file

@ -1,23 +1,30 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
{
meta.maintainers = [ lib.maintainers.rycee ];
options.programs = let
description = ''
Whether to enable integration with terminals using the VTE
library. This will let the terminal track the current working
directory.
'';
in {
bash.enableVteIntegration = lib.mkEnableOption "" // {
inherit description;
};
options.programs =
let
description = ''
Whether to enable integration with terminals using the VTE
library. This will let the terminal track the current working
directory.
'';
in
{
bash.enableVteIntegration = lib.mkEnableOption "" // {
inherit description;
};
zsh.enableVteIntegration = lib.mkEnableOption "" // {
inherit description;
zsh.enableVteIntegration = lib.mkEnableOption "" // {
inherit description;
};
};
};
config = lib.mkMerge [
(lib.mkIf config.programs.bash.enableVteIntegration {

View file

@ -1,7 +1,23 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (builtins) baseNameOf listToAttrs map unsafeDiscardStringContext;
inherit (lib) literalExpression mkEnableOption mkIf mkOption types;
inherit (builtins)
baseNameOf
listToAttrs
map
unsafeDiscardStringContext
;
inherit (lib)
literalExpression
mkEnableOption
mkIf
mkOption
types
;
cfg = config.xdg.autostart;
@ -10,7 +26,8 @@ let
${lib.concatMapStringsSep "\n" (e: "ln -s ${e} $out") cfg.entries}
'';
in {
in
{
meta.maintainers = with lib.maintainers; [ Scrumplex ];
options.xdg.autostart = {

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) literalExpression mkOption types;
@ -6,9 +11,9 @@ let
desktopEntry = {
imports = [
(lib.mkRemovedOptionModule [ "extraConfig" ]
"The `extraConfig` option of `xdg.desktopEntries` has been removed following a change in Nixpkgs.")
(lib.mkRemovedOptionModule [ "fileValidation" ]
"Validation of the desktop file is always enabled.")
"The `extraConfig` option of `xdg.desktopEntries` has been removed following a change in Nixpkgs."
)
(lib.mkRemovedOptionModule [ "fileValidation" ] "Validation of the desktop file is always enabled.")
];
options = {
# Since this module uses the nixpkgs/pkgs/build-support/make-desktopitem function,
@ -28,7 +33,11 @@ let
type = mkOption {
description = "The type of the desktop entry.";
default = "Application";
type = types.enum [ "Application" "Link" "Directory" ];
type = types.enum [
"Application"
"Link"
"Directory"
];
};
exec = mkOption {
@ -73,8 +82,7 @@ let
};
categories = mkOption {
description =
"Categories in which the entry should be shown in a menu.";
description = "Categories in which the entry should be shown in a menu.";
type = types.nullOr (types.listOf types.str);
default = null;
};
@ -122,24 +130,29 @@ let
};
actions = mkOption {
type = types.attrsOf (types.submodule ({ name, ... }: {
options.name = mkOption {
type = types.str;
default = name;
defaultText = literalExpression "<name>";
description = "Name of the action.";
};
options.exec = mkOption {
type = types.nullOr types.str;
description = "Program to execute, possibly with arguments.";
default = null;
};
options.icon = mkOption {
type = with types; nullOr (either str path);
default = null;
description = "Icon to display in file manager, menus, etc.";
};
}));
type = types.attrsOf (
types.submodule (
{ name, ... }:
{
options.name = mkOption {
type = types.str;
default = name;
defaultText = literalExpression "<name>";
description = "Name of the action.";
};
options.exec = mkOption {
type = types.nullOr types.str;
description = "Program to execute, possibly with arguments.";
default = null;
};
options.icon = mkOption {
type = with types; nullOr (either str path);
default = null;
description = "Icon to display in file manager, menus, etc.";
};
}
)
);
default = { };
defaultText = literalExpression "{ }";
example = literalExpression ''
@ -149,8 +162,7 @@ let
};
}
'';
description =
"The set of actions made available to application launchers.";
description = "The set of actions made available to application launchers.";
};
# Required for the assertions
@ -165,18 +177,29 @@ let
};
#passes config options to makeDesktopItem in expected format
makeFile = name: config:
makeFile =
name: config:
pkgs.makeDesktopItem {
inherit name;
inherit (config)
type exec icon comment terminal genericName startupNotify noDisplay
prefersNonDefaultGPU actions;
type
exec
icon
comment
terminal
genericName
startupNotify
noDisplay
prefersNonDefaultGPU
actions
;
desktopName = config.name;
mimeTypes = lib.optionals (config.mimeType != null) config.mimeType;
categories = lib.optionals (config.categories != null) config.categories;
extraConfig = config.settings;
};
in {
in
{
meta.maintainers = [ lib.hm.maintainers.cwyc ];
options.xdg.desktopEntries = mkOption {
@ -205,14 +228,13 @@ in {
config = lib.mkIf (config.xdg.desktopEntries != { }) {
assertions = [
(lib.hm.assertions.assertPlatform "xdg.desktopEntries" pkgs
lib.platforms.linux)
] ++ lib.flatten
(lib.catAttrs "assertions" (lib.attrValues config.xdg.desktopEntries));
(lib.hm.assertions.assertPlatform "xdg.desktopEntries" pkgs lib.platforms.linux)
] ++ lib.flatten (lib.catAttrs "assertions" (lib.attrValues config.xdg.desktopEntries));
home.packages =
(map lib.hiPrio # we need hiPrio to override existing entries
(lib.attrsets.mapAttrsToList makeFile config.xdg.desktopEntries));
home.packages = (
map lib.hiPrio # we need hiPrio to override existing entries
(lib.attrsets.mapAttrsToList makeFile config.xdg.desktopEntries)
);
};
}

View file

@ -1,14 +1,19 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkOption types;
cfg = config.xdg.mimeApps;
strListOrSingleton = with types;
coercedTo (either (listOf str) str) lib.toList (listOf str);
strListOrSingleton = with types; coercedTo (either (listOf str) str) lib.toList (listOf str);
in {
in
{
meta.maintainers = with lib.maintainers; [ euxane ];
options.xdg.mimeApps = {
@ -44,7 +49,9 @@ in {
associations.removed = mkOption {
type = types.attrsOf strListOrSingleton;
default = { };
example = { "mimetype1" = "foo5.desktop"; };
example = {
"mimetype1" = "foo5.desktop";
};
description = ''
Removes associations of applications with mimetypes, as if the
.desktop file was *not* listing this
@ -75,43 +82,47 @@ in {
# Given a package that installs .desktop files in the usual location,
# return a mapping from mime types to lists of desktop file names. This is
# suitable for use with `xdg.mimeApps.defaultApplications`.
lib.xdg.mimeAssociations = let
processLines = str:
lib.zipAttrs (lib.filter (e: e != null)
(map processLine (lib.splitString "\n" str)));
lib.xdg.mimeAssociations =
let
processLines =
str: lib.zipAttrs (lib.filter (e: e != null) (map processLine (lib.splitString "\n" str)));
processLine = str:
let
entry = lib.splitString ";" str;
k = lib.elemAt entry 0;
v = lib.elemAt entry 1;
in if lib.length entry == 2 then { ${k} = v; } else null;
processLine =
str:
let
entry = lib.splitString ";" str;
k = lib.elemAt entry 0;
v = lib.elemAt entry 1;
in
if lib.length entry == 2 then { ${k} = v; } else null;
associations = ps:
pkgs.runCommand "mime-assoc" { inherit ps; } ''
for p in $ps ; do
for path in "$p"/share/applications/*.desktop ; do
name="''${path##*/}"
sed -n -E "/^MimeType=/ { s/.*=//; s/;?$|;/;$name\n/g; p; }" "$path"
done
done > "$out"
'';
in p: processLines (builtins.readFile (associations p));
associations =
ps:
pkgs.runCommand "mime-assoc" { inherit ps; } ''
for p in $ps ; do
for path in "$p"/share/applications/*.desktop ; do
name="''${path##*/}"
sed -n -E "/^MimeType=/ { s/.*=//; s/;?$|;/;$name\n/g; p; }" "$path"
done
done > "$out"
'';
in
p: processLines (builtins.readFile (associations p));
}
(lib.mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "xdg.mimeApps" pkgs
lib.platforms.linux)
(lib.hm.assertions.assertPlatform "xdg.mimeApps" pkgs lib.platforms.linux)
];
# Deprecated but still used by some applications.
xdg.dataFile."applications/mimeapps.list".source =
config.xdg.configFile."mimeapps.list".source;
xdg.dataFile."applications/mimeapps.list".source = config.xdg.configFile."mimeapps.list".source;
xdg.configFile."mimeapps.list".text =
let joinValues = lib.mapAttrs (n: lib.concatStringsSep ";");
in lib.generators.toINI { } {
let
joinValues = lib.mapAttrs (n: lib.concatStringsSep ";");
in
lib.generators.toINI { } {
"Added Associations" = joinValues cfg.associations.added;
"Removed Associations" = joinValues cfg.associations.removed;
"Default Applications" = joinValues cfg.defaultApplications;

View file

@ -1,19 +1,29 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.xdg.mime;
inherit (lib) getExe getExe' mkOption types;
inherit (lib)
getExe
getExe'
mkOption
types
;
in {
in
{
options = {
xdg.mime = {
enable = mkOption {
type = types.bool;
default = pkgs.stdenv.hostPlatform.isLinux;
defaultText = lib.literalExpression
"true if host platform is Linux, false otherwise";
defaultText = lib.literalExpression "true if host platform is Linux, false otherwise";
description = ''
Whether to install programs and files to support the
XDG Shared MIME-info specification and XDG MIME Applications
@ -36,8 +46,7 @@ in {
type = types.package;
default = pkgs.desktop-file-utils;
defaultText = lib.literalExpression "pkgs.desktop-file-utils";
description =
"The package to use when running update-desktop-database.";
description = "The package to use when running update-desktop-database.";
};
};
};
@ -64,16 +73,14 @@ in {
XDG_DATA_DIRS=$out/share \
PKGSYSTEM_ENABLE_FSYNC=0 \
${
getExe
(cfg.sharedMimeInfoPackage.__spliced.buildHost or cfg.sharedMimeInfoPackage)
getExe (cfg.sharedMimeInfoPackage.__spliced.buildHost or cfg.sharedMimeInfoPackage)
} -V $out/share/mime > /dev/null
fi
if [[ -w $out/share/applications ]]; then
${
getExe'
(cfg.desktopFileUtilsPackage.__spliced.buildHost or cfg.desktopFileUtilsPackage)
"update-desktop-database"
getExe' (cfg.desktopFileUtilsPackage.__spliced.buildHost or cfg.desktopFileUtilsPackage
) "update-desktop-database"
} $out/share/applications
fi
'';

View file

@ -1,14 +1,26 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkIf mkMerge mkOption optional types;
inherit (lib)
mkIf
mkMerge
mkOption
optional
types
;
associationOptions = with types;
attrsOf (coercedTo (either (listOf str) str)
(x: lib.concatStringsSep ";" (lib.toList x)) str);
associationOptions =
with types;
attrsOf (coercedTo (either (listOf str) str) (x: lib.concatStringsSep ";" (lib.toList x)) str);
in {
in
{
meta.maintainers = [ lib.maintainers.misterio77 ];
options.xdg.portal = {
@ -63,12 +75,22 @@ in {
type = types.attrsOf associationOptions;
default = { };
example = {
x-cinnamon = { default = [ "xapp" "gtk" ]; };
x-cinnamon = {
default = [
"xapp"
"gtk"
];
};
pantheon = {
default = [ "pantheon" "gtk" ];
default = [
"pantheon"
"gtk"
];
"org.freedesktop.impl.portal.Secret" = [ "gnome-keyring" ];
};
common = { default = [ "gtk" ]; };
common = {
default = [ "gtk" ];
};
};
description = ''
Sets which portal backend should be used to provide the implementation
@ -97,51 +119,53 @@ in {
};
};
config = let
cfg = config.xdg.portal;
packages = [ pkgs.xdg-desktop-portal ] ++ cfg.extraPortals;
portalsDir =
"${config.home.profileDirectory}/share/xdg-desktop-portal/portals";
in mkIf cfg.enable {
warnings = optional (cfg.configPackages == [ ] && cfg.config == { }) ''
xdg-desktop-portal 1.17 reworked how portal implementations are loaded, you
should either set `xdg.portal.config` or `xdg.portal.configPackages`
to specify which portal backend to use for the requested interface.
config =
let
cfg = config.xdg.portal;
packages = [ pkgs.xdg-desktop-portal ] ++ cfg.extraPortals;
portalsDir = "${config.home.profileDirectory}/share/xdg-desktop-portal/portals";
in
mkIf cfg.enable {
warnings = optional (cfg.configPackages == [ ] && cfg.config == { }) ''
xdg-desktop-portal 1.17 reworked how portal implementations are loaded, you
should either set `xdg.portal.config` or `xdg.portal.configPackages`
to specify which portal backend to use for the requested interface.
https://github.com/flatpak/xdg-desktop-portal/blob/1.18.1/doc/portals.conf.rst.in
https://github.com/flatpak/xdg-desktop-portal/blob/1.18.1/doc/portals.conf.rst.in
If you simply want to keep the behaviour in < 1.17, which uses the first
portal implementation found in lexicographical order, use the following:
If you simply want to keep the behaviour in < 1.17, which uses the first
portal implementation found in lexicographical order, use the following:
xdg.portal.config.common.default = "*";
'';
xdg.portal.config.common.default = "*";
'';
assertions = [
(lib.hm.assertions.assertPlatform "xdg.portal" pkgs lib.platforms.linux)
assertions = [
(lib.hm.assertions.assertPlatform "xdg.portal" pkgs lib.platforms.linux)
{
assertion = cfg.extraPortals != [ ];
message =
"Setting xdg.portal.enable to true requires a portal implementation in xdg.portal.extraPortals such as xdg-desktop-portal-gtk or xdg-desktop-portal-kde.";
}
];
home = {
packages = packages ++ cfg.configPackages;
sessionVariables = mkMerge [
(mkIf cfg.xdgOpenUsePortal { NIXOS_XDG_OPEN_USE_PORTAL = "1"; })
{ NIX_XDG_DESKTOP_PORTAL_DIR = portalsDir; }
{
assertion = cfg.extraPortals != [ ];
message = "Setting xdg.portal.enable to true requires a portal implementation in xdg.portal.extraPortals such as xdg-desktop-portal-gtk or xdg-desktop-portal-kde.";
}
];
};
systemd.user.sessionVariables = {
NIX_XDG_DESKTOP_PORTAL_DIR = portalsDir;
};
xdg.configFile = lib.concatMapAttrs (desktop: conf:
lib.optionalAttrs (conf != { }) {
"xdg-desktop-portal/${
lib.optionalString (desktop != "common") "${desktop}-"
}portals.conf".text = lib.generators.toINI { } { preferred = conf; };
}) cfg.config;
};
home = {
packages = packages ++ cfg.configPackages;
sessionVariables = mkMerge [
(mkIf cfg.xdgOpenUsePortal { NIXOS_XDG_OPEN_USE_PORTAL = "1"; })
{ NIX_XDG_DESKTOP_PORTAL_DIR = portalsDir; }
];
};
systemd.user.sessionVariables = {
NIX_XDG_DESKTOP_PORTAL_DIR = portalsDir;
};
xdg.configFile = lib.concatMapAttrs (
desktop: conf:
lib.optionalAttrs (conf != { }) {
"xdg-desktop-portal/${lib.optionalString (desktop != "common") "${desktop}-"}portals.conf".text =
lib.generators.toINI { }
{ preferred = conf; };
}
) cfg.config;
};
}

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) types;
@ -9,7 +14,8 @@ let
dataDirs = lib.concatStringsSep ":" cfg.data;
in {
in
{
meta.maintainers = with lib.maintainers; [ tadfisher ];
options.xdg.systemDirs = {
@ -37,25 +43,20 @@ in {
config = lib.mkMerge [
(lib.mkIf (cfg.config != [ ] || cfg.data != [ ]) {
assertions = [
(lib.hm.assertions.assertPlatform "xdg.systemDirs" pkgs
lib.platforms.linux)
(lib.hm.assertions.assertPlatform "xdg.systemDirs" pkgs lib.platforms.linux)
];
})
(lib.mkIf (cfg.config != [ ]) {
home.sessionVariables.XDG_CONFIG_DIRS =
"${configDirs}\${XDG_CONFIG_DIRS:+:$XDG_CONFIG_DIRS}";
home.sessionVariables.XDG_CONFIG_DIRS = "${configDirs}\${XDG_CONFIG_DIRS:+:$XDG_CONFIG_DIRS}";
systemd.user.sessionVariables.XDG_CONFIG_DIRS =
"${configDirs}\${XDG_CONFIG_DIRS:+:$XDG_CONFIG_DIRS}";
systemd.user.sessionVariables.XDG_CONFIG_DIRS = "${configDirs}\${XDG_CONFIG_DIRS:+:$XDG_CONFIG_DIRS}";
})
(lib.mkIf (cfg.data != [ ]) {
home.sessionVariables.XDG_DATA_DIRS =
"${dataDirs}\${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}";
home.sessionVariables.XDG_DATA_DIRS = "${dataDirs}\${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}";
systemd.user.sessionVariables.XDG_DATA_DIRS =
"${dataDirs}\${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}";
systemd.user.sessionVariables.XDG_DATA_DIRS = "${dataDirs}\${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}";
})
];
}

View file

@ -1,19 +1,28 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) literalExpression mkOption types;
cfg = config.xdg.userDirs;
in {
in
{
meta.maintainers = with lib.maintainers; [ euxane ];
imports = [
(lib.mkRenamedOptionModule [ "xdg" "userDirs" "publishShare" ] [
"xdg"
"userDirs"
"publicShare"
])
(lib.mkRenamedOptionModule
[ "xdg" "userDirs" "publishShare" ]
[
"xdg"
"userDirs"
"publicShare"
]
)
];
options.xdg.userDirs = {
@ -33,64 +42,56 @@ in {
desktop = mkOption {
type = with types; nullOr (coercedTo path toString str);
default = "${config.home.homeDirectory}/Desktop";
defaultText =
literalExpression ''"''${config.home.homeDirectory}/Desktop"'';
defaultText = literalExpression ''"''${config.home.homeDirectory}/Desktop"'';
description = "The Desktop directory.";
};
documents = mkOption {
type = with types; nullOr (coercedTo path toString str);
default = "${config.home.homeDirectory}/Documents";
defaultText =
literalExpression ''"''${config.home.homeDirectory}/Documents"'';
defaultText = literalExpression ''"''${config.home.homeDirectory}/Documents"'';
description = "The Documents directory.";
};
download = mkOption {
type = with types; nullOr (coercedTo path toString str);
default = "${config.home.homeDirectory}/Downloads";
defaultText =
literalExpression ''"''${config.home.homeDirectory}/Downloads"'';
defaultText = literalExpression ''"''${config.home.homeDirectory}/Downloads"'';
description = "The Downloads directory.";
};
music = mkOption {
type = with types; nullOr (coercedTo path toString str);
default = "${config.home.homeDirectory}/Music";
defaultText =
literalExpression ''"''${config.home.homeDirectory}/Music"'';
defaultText = literalExpression ''"''${config.home.homeDirectory}/Music"'';
description = "The Music directory.";
};
pictures = mkOption {
type = with types; nullOr (coercedTo path toString str);
default = "${config.home.homeDirectory}/Pictures";
defaultText =
literalExpression ''"''${config.home.homeDirectory}/Pictures"'';
defaultText = literalExpression ''"''${config.home.homeDirectory}/Pictures"'';
description = "The Pictures directory.";
};
publicShare = mkOption {
type = with types; nullOr (coercedTo path toString str);
default = "${config.home.homeDirectory}/Public";
defaultText =
literalExpression ''"''${config.home.homeDirectory}/Public"'';
defaultText = literalExpression ''"''${config.home.homeDirectory}/Public"'';
description = "The Public share directory.";
};
templates = mkOption {
type = with types; nullOr (coercedTo path toString str);
default = "${config.home.homeDirectory}/Templates";
defaultText =
literalExpression ''"''${config.home.homeDirectory}/Templates"'';
defaultText = literalExpression ''"''${config.home.homeDirectory}/Templates"'';
description = "The Templates directory.";
};
videos = mkOption {
type = with types; nullOr (coercedTo path toString str);
default = "${config.home.homeDirectory}/Videos";
defaultText =
literalExpression ''"''${config.home.homeDirectory}/Videos"'';
defaultText = literalExpression ''"''${config.home.homeDirectory}/Videos"'';
description = "The Videos directory.";
};
@ -106,41 +107,48 @@ in {
description = "Other user directories.";
};
createDirectories =
lib.mkEnableOption "automatic creation of the XDG user directories";
createDirectories = lib.mkEnableOption "automatic creation of the XDG user directories";
};
config = let
directories = (lib.filterAttrs (n: v: !isNull v) {
XDG_DESKTOP_DIR = cfg.desktop;
XDG_DOCUMENTS_DIR = cfg.documents;
XDG_DOWNLOAD_DIR = cfg.download;
XDG_MUSIC_DIR = cfg.music;
XDG_PICTURES_DIR = cfg.pictures;
XDG_PUBLICSHARE_DIR = cfg.publicShare;
XDG_TEMPLATES_DIR = cfg.templates;
XDG_VIDEOS_DIR = cfg.videos;
}) // cfg.extraConfig;
in lib.mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "xdg.userDirs" pkgs lib.platforms.linux)
];
config =
let
directories =
(lib.filterAttrs (n: v: !isNull v) {
XDG_DESKTOP_DIR = cfg.desktop;
XDG_DOCUMENTS_DIR = cfg.documents;
XDG_DOWNLOAD_DIR = cfg.download;
XDG_MUSIC_DIR = cfg.music;
XDG_PICTURES_DIR = cfg.pictures;
XDG_PUBLICSHARE_DIR = cfg.publicShare;
XDG_TEMPLATES_DIR = cfg.templates;
XDG_VIDEOS_DIR = cfg.videos;
})
// cfg.extraConfig;
in
lib.mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "xdg.userDirs" pkgs lib.platforms.linux)
];
xdg.configFile."user-dirs.dirs".text = let
# For some reason, these need to be wrapped with quotes to be valid.
wrapped = lib.mapAttrs (_: value: ''"${value}"'') directories;
in lib.generators.toKeyValue { } wrapped;
xdg.configFile."user-dirs.dirs".text =
let
# For some reason, these need to be wrapped with quotes to be valid.
wrapped = lib.mapAttrs (_: value: ''"${value}"'') directories;
in
lib.generators.toKeyValue { } wrapped;
xdg.configFile."user-dirs.conf".text = "enabled=False";
xdg.configFile."user-dirs.conf".text = "enabled=False";
home.sessionVariables = directories;
home.sessionVariables = directories;
home.activation.createXdgUserDirectories = lib.mkIf cfg.createDirectories
(let
directoriesList = lib.attrValues directories;
mkdir =
(dir: ''[[ -L "${dir}" ]] || run mkdir -p $VERBOSE_ARG "${dir}"'');
in lib.hm.dag.entryAfter [ "linkGeneration" ]
(lib.strings.concatMapStringsSep "\n" mkdir directoriesList));
};
home.activation.createXdgUserDirectories = lib.mkIf cfg.createDirectories (
let
directoriesList = lib.attrValues directories;
mkdir = (dir: ''[[ -L "${dir}" ]] || run mkdir -p $VERBOSE_ARG "${dir}"'');
in
lib.hm.dag.entryAfter [ "linkGeneration" ] (
lib.strings.concatMapStringsSep "\n" mkdir directoriesList
)
);
};
}

View file

@ -1,25 +1,40 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkOptionDefault mkIf mkOption types;
inherit (lib)
mkOptionDefault
mkIf
mkOption
types
;
cfg = config.xdg;
fileType = (import ../lib/file-type.nix {
inherit (config.home) homeDirectory;
inherit lib pkgs;
}).fileType;
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";
defaultStateHome = "${config.home.homeDirectory}/.local/state";
getEnvFallback = name: fallback:
let value = builtins.getEnv name;
in if value != "" then value else fallback;
getEnvFallback =
name: fallback:
let
value = builtins.getEnv name;
in
if value != "" then value else fallback;
in {
in
{
options.xdg = {
enable = lib.mkEnableOption "management of XDG base directories";
@ -64,8 +79,7 @@ in {
};
dataFile = mkOption {
type =
fileType "xdg.dataFile" "<varname>xdg.dataHome</varname>" cfg.dataHome;
type = fileType "xdg.dataFile" "<varname>xdg.dataHome</varname>" cfg.dataHome;
default = { };
description = ''
Attribute set of files to link into the user's XDG
@ -85,8 +99,7 @@ in {
};
stateFile = mkOption {
type = fileType "xdg.stateFile" "<varname>xdg.stateHome</varname>"
cfg.stateHome;
type = fileType "xdg.stateFile" "<varname>xdg.stateHome</varname>" cfg.stateHome;
default = { };
description = ''
Attribute set of files to link into the user's XDG
@ -107,34 +120,32 @@ in {
};
config = lib.mkMerge [
(let
variables = {
XDG_CACHE_HOME = cfg.cacheHome;
XDG_CONFIG_HOME = cfg.configHome;
XDG_DATA_HOME = cfg.dataHome;
XDG_STATE_HOME = cfg.stateHome;
};
in mkIf cfg.enable {
xdg.cacheHome = mkOptionDefault defaultCacheHome;
xdg.configHome = mkOptionDefault defaultConfigHome;
xdg.dataHome = mkOptionDefault defaultDataHome;
xdg.stateHome = mkOptionDefault defaultStateHome;
(
let
variables = {
XDG_CACHE_HOME = cfg.cacheHome;
XDG_CONFIG_HOME = cfg.configHome;
XDG_DATA_HOME = cfg.dataHome;
XDG_STATE_HOME = cfg.stateHome;
};
in
mkIf cfg.enable {
xdg.cacheHome = mkOptionDefault defaultCacheHome;
xdg.configHome = mkOptionDefault defaultConfigHome;
xdg.dataHome = mkOptionDefault defaultDataHome;
xdg.stateHome = mkOptionDefault defaultStateHome;
home.sessionVariables = variables;
systemd.user.sessionVariables =
mkIf pkgs.stdenv.hostPlatform.isLinux variables;
})
home.sessionVariables = variables;
systemd.user.sessionVariables = mkIf pkgs.stdenv.hostPlatform.isLinux variables;
}
)
# Legacy non-deterministic setup.
(mkIf (!cfg.enable && lib.versionOlder config.home.stateVersion "20.09") {
xdg.cacheHome =
mkOptionDefault (getEnvFallback "XDG_CACHE_HOME" defaultCacheHome);
xdg.configHome =
mkOptionDefault (getEnvFallback "XDG_CONFIG_HOME" defaultConfigHome);
xdg.dataHome =
mkOptionDefault (getEnvFallback "XDG_DATA_HOME" defaultDataHome);
xdg.stateHome =
mkOptionDefault (getEnvFallback "XDG_STATE_HOME" defaultStateHome);
xdg.cacheHome = mkOptionDefault (getEnvFallback "XDG_CACHE_HOME" defaultCacheHome);
xdg.configHome = mkOptionDefault (getEnvFallback "XDG_CONFIG_HOME" defaultConfigHome);
xdg.dataHome = mkOptionDefault (getEnvFallback "XDG_DATA_HOME" defaultDataHome);
xdg.stateHome = mkOptionDefault (getEnvFallback "XDG_STATE_HOME" defaultStateHome);
})
# "Modern" deterministic setup.
@ -147,18 +158,10 @@ in {
{
home.file = lib.mkMerge [
(lib.mapAttrs'
(name: file: lib.nameValuePair "${cfg.cacheHome}/${name}" file)
cfg.cacheFile)
(lib.mapAttrs'
(name: file: lib.nameValuePair "${cfg.configHome}/${name}" file)
cfg.configFile)
(lib.mapAttrs'
(name: file: lib.nameValuePair "${cfg.dataHome}/${name}" file)
cfg.dataFile)
(lib.mapAttrs'
(name: file: lib.nameValuePair "${cfg.stateHome}/${name}" file)
cfg.stateFile)
(lib.mapAttrs' (name: file: lib.nameValuePair "${cfg.cacheHome}/${name}" file) cfg.cacheFile)
(lib.mapAttrs' (name: file: lib.nameValuePair "${cfg.configHome}/${name}" file) cfg.configFile)
(lib.mapAttrs' (name: file: lib.nameValuePair "${cfg.dataHome}/${name}" file) cfg.dataFile)
(lib.mapAttrs' (name: file: lib.nameValuePair "${cfg.stateHome}/${name}" file) cfg.stateFile)
{ "${cfg.cacheHome}/.keep".text = ""; }
{ "${cfg.stateHome}/.keep".text = ""; }
];

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkOption types;
@ -8,7 +13,11 @@ let
xfIntVariant = types.submodule {
options = {
type = mkOption {
type = types.enum [ "int" "uint" "uint64" ];
type = types.enum [
"int"
"uint"
"uint64"
];
description = ''
To distinguish between int, uint and uint64 in xfconf,
you can specify the type in xfconf with this submodule.
@ -23,38 +32,50 @@ let
};
};
withType = v:
if builtins.isAttrs v then [
"-t"
v.type
"-s"
(toString v.value)
] else if builtins.isBool v then [
"-t"
"bool"
"-s"
(if v then "true" else "false")
] else if builtins.isInt v then [
"-t"
"int"
"-s"
(toString v)
] else if builtins.isFloat v then [
"-t"
"double"
"-s"
(toString v)
] else if builtins.isString v then [
"-t"
"string"
"-s"
v
] else if builtins.isList v then
withType =
v:
if builtins.isAttrs v then
[
"-t"
v.type
"-s"
(toString v.value)
]
else if builtins.isBool v then
[
"-t"
"bool"
"-s"
(if v then "true" else "false")
]
else if builtins.isInt v then
[
"-t"
"int"
"-s"
(toString v)
]
else if builtins.isFloat v then
[
"-t"
"double"
"-s"
(toString v)
]
else if builtins.isString v then
[
"-t"
"string"
"-s"
v
]
else if builtins.isList v then
[ "-a" ] ++ lib.concatMap withType v
else
throw "unexpected xfconf type: ${builtins.typeOf v}";
in {
in
{
meta.maintainers = [ lib.maintainers.chuangzhu ];
options.xfconf = {
@ -73,10 +94,20 @@ in {
};
settings = mkOption {
type = with types;
# xfIntVariant must come AFTER str; otherwise strings are treated as submodule imports...
let value = nullOr (oneOf [ bool int float str xfIntVariant ]);
in attrsOf (attrsOf (either value (listOf value))) // {
type =
with types;
# xfIntVariant must come AFTER str; otherwise strings are treated as submodule imports...
let
value = nullOr (oneOf [
bool
int
float
str
xfIntVariant
]);
in
attrsOf (attrsOf (either value (listOf value)))
// {
description = "xfconf settings";
};
default = { };
@ -99,30 +130,33 @@ in {
};
config = lib.mkIf (cfg.enable && cfg.settings != { }) {
assertions =
[ (lib.hm.assertions.assertPlatform "xfconf" pkgs lib.platforms.linux) ];
assertions = [ (lib.hm.assertions.assertPlatform "xfconf" pkgs lib.platforms.linux) ];
home.activation.xfconfSettings = lib.hm.dag.entryAfter [ "installPackages" ]
(let
home.activation.xfconfSettings = lib.hm.dag.entryAfter [ "installPackages" ] (
let
mkCommand = channel: property: value: ''
run ${pkgs.xfce.xfconf}/bin/xfconf-query \
${
lib.escapeShellArgs ([ "-c" channel "-p" "/${property}" ]
++ (if value == null then
[ "-r" ]
else
[ "-n" ] ++ withType value))
}
${lib.escapeShellArgs (
[
"-c"
channel
"-p"
"/${property}"
]
++ (if value == null then [ "-r" ] else [ "-n" ] ++ withType value)
)}
'';
commands = lib.mapAttrsToList (channel: properties:
lib.mapAttrsToList (mkCommand channel) properties) cfg.settings;
commands = lib.mapAttrsToList (
channel: properties: lib.mapAttrsToList (mkCommand channel) properties
) cfg.settings;
load = pkgs.writeShellScript "load-xfconf" ''
${config.lib.bash.initHomeManagerLib}
${lib.concatMapStrings lib.concatStrings commands}
'';
in ''
in
''
if [[ -v DBUS_SESSION_BUS_ADDRESS ]]; then
export DBUS_RUN_SESSION_CMD=""
else
@ -132,6 +166,7 @@ in {
run $DBUS_RUN_SESSION_CMD ${load}
unset DBUS_RUN_SESSION_CMD
'');
''
);
};
}

View file

@ -1,492 +1,499 @@
{ pkgs
{
pkgs,
# Note, this should be "the standard library" + HM extensions.
, lib
# Note, this should be "the standard library" + HM extensions.
lib,
# Whether to enable module type checking.
, check ? true
# Whether to enable module type checking.
check ? true,
# If disabled, the pkgs attribute passed to this function is used instead.
, useNixpkgsModule ? true }:
useNixpkgsModule ? true,
}:
let
modules = [
./accounts/email.nix
./accounts/calendar.nix
./accounts/contacts.nix
./config/home-cursor.nix
./config/i18n.nix
./files.nix
./home-environment.nix
./i18n/input-method/default.nix
./launchd/default.nix
./manual.nix
./misc/dconf.nix
./misc/debug.nix
./misc/editorconfig.nix
./misc/fontconfig.nix
./misc/gtk.nix
./misc/lib.nix
./misc/mozilla-messaging-hosts.nix
./misc/news.nix
./misc/nixgl.nix
./misc/numlock.nix
./misc/pam.nix
./misc/qt.nix
./misc/qt/kconfig.nix
./misc/shell.nix
./misc/specialisation.nix
./misc/submodule-support.nix
./misc/tmpfiles.nix
./misc/uninstall.nix
./misc/version.nix
./misc/vte.nix
./misc/xdg-autostart.nix
./misc/xdg-desktop-entries.nix
./misc/xdg-mime-apps.nix
./misc/xdg-mime.nix
./misc/xdg-portal.nix
./misc/xdg-system-dirs.nix
./misc/xdg-user-dirs.nix
./misc/xdg.nix
./misc/xfconf.nix
./programs/abook.nix
./programs/aerc.nix
./programs/aerospace.nix
./programs/afew.nix
./programs/alacritty.nix
./programs/alot.nix
./programs/antidote.nix
./programs/aria2.nix
./programs/astroid.nix
./programs/atuin.nix
./programs/autojump.nix
./programs/autorandr.nix
./programs/awscli.nix
./programs/bash.nix
./programs/bashmount.nix
./programs/bat.nix
./programs/bacon.nix
./programs/beets.nix
./programs/bemenu.nix
./programs/borgmatic.nix
./programs/bottom.nix
./programs/boxxy.nix
./programs/broot.nix
./programs/browserpass.nix
./programs/btop.nix
./programs/bun.nix
./programs/carapace.nix
./programs/cava.nix
./programs/cavalier.nix
./programs/chromium.nix
./programs/cmus.nix
./programs/command-not-found/command-not-found.nix
./programs/comodoro.nix
./programs/darcs.nix
./programs/dircolors.nix
./programs/direnv.nix
./programs/discocss.nix
./programs/distrobox.nix
./programs/earthly.nix
./programs/eclipse.nix
./programs/emacs.nix
./programs/eww.nix
./programs/eza.nix
./programs/fastfetch.nix
./programs/fd.nix
./programs/feh.nix
./programs/firefox.nix
./programs/fish.nix
./programs/floorp.nix
./programs/foot.nix
./programs/freetube.nix
./programs/fuzzel.nix
./programs/fzf.nix
./programs/gallery-dl.nix
./programs/getmail.nix
./programs/gh.nix
./programs/gh-dash.nix
./programs/ghostty.nix
./programs/git-cliff.nix
./programs/git-credential-oauth.nix
./programs/git-worktree-switcher.nix
./programs/git.nix
./programs/gitui.nix
./programs/gnome-shell.nix
./programs/gnome-terminal.nix
./programs/go.nix
./programs/gpg.nix
./programs/gradle.nix
./programs/granted.nix
./programs/havoc.nix
./programs/helix.nix
./programs/hexchat.nix
./programs/himalaya.nix
./programs/home-manager.nix
./programs/hstr.nix
./programs/htop.nix
./programs/hyfetch.nix
./programs/hyprlock.nix
./programs/i3blocks.nix
./programs/i3status-rust.nix
./programs/i3status.nix
./programs/iamb.nix
./programs/imv.nix
./programs/info.nix
./programs/ion.nix
./programs/irssi.nix
./programs/java.nix
./programs/jetbrains-remote.nix
./programs/jq.nix
./programs/jqp.nix
./programs/jujutsu.nix
./programs/joshuto.nix
./programs/joplin-desktop.nix
./programs/just.nix
./programs/k9s.nix
./programs/kakoune.nix
./programs/keychain.nix
./programs/khal.nix
./programs/khard.nix
./programs/kitty.nix
./programs/kodi.nix
./programs/kubecolor.nix
./programs/lapce.nix
./programs/lazydocker.nix
./programs/lazygit.nix
./programs/ledger.nix
./programs/less.nix
./programs/lesspipe.nix
./programs/lf.nix
./programs/librewolf.nix
./programs/lieer.nix
./programs/looking-glass-client.nix
./programs/lsd.nix
./programs/man.nix
./programs/mangohud.nix
./programs/matplotlib.nix
./programs/mbsync.nix
./programs/mcfly.nix
./programs/mercurial.nix
./programs/mergiraf.nix
./programs/micro.nix
./programs/mise.nix
./programs/mods.nix
./programs/mpv.nix
./programs/mr.nix
./programs/msmtp.nix
./programs/mu.nix
./programs/mujmap.nix
./programs/navi.nix
./programs/ncmpcpp.nix
./programs/ncspot.nix
./programs/ne.nix
./programs/neomutt.nix
./programs/neovide.nix
./programs/neovim.nix
./programs/newsboat.nix
./programs/nh.nix
./programs/nheko.nix
./programs/nix-index.nix
./programs/nix-your-shell.nix
./programs/nnn.nix
./programs/noti.nix
./programs/notmuch.nix
./programs/nushell.nix
./programs/obs-studio.nix
./programs/octant.nix
./programs/offlineimap.nix
./programs/oh-my-posh.nix
./programs/onlyoffice.nix
./programs/opam.nix
./programs/openstackclient.nix
./programs/pandoc.nix
./programs/papis.nix
./programs/password-store.nix
./programs/pay-respects.nix
./programs/pazi.nix
./programs/pet.nix
./programs/pidgin.nix
./programs/pistol.nix
./programs/piston-cli.nix
./programs/pls.nix
./programs/poetry.nix
./programs/powerline-go.nix
./programs/pqiv.nix
./programs/pubs.nix
./programs/pyenv.nix
./programs/pylint.nix
./programs/qcal.nix
./programs/qutebrowser.nix
./programs/ranger.nix
./programs/rbw.nix
./programs/rclone.nix
./programs/readline.nix
./programs/rio.nix
./programs/ripgrep.nix
./programs/ripgrep-all.nix
./programs/rofi-pass.nix
./programs/rofi.nix
./programs/rtorrent.nix
./programs/ruff.nix
./programs/sagemath.nix
./programs/sapling.nix
./programs/sbt.nix
./programs/scmpuff.nix
./programs/script-directory.nix
./programs/senpai.nix
./programs/sesh.nix
./programs/sftpman.nix
./programs/sioyek.nix
./programs/skim.nix
./programs/sm64ex.nix
./programs/smug.nix
./programs/spotify-player.nix
./programs/sqls.nix
./programs/ssh.nix
./programs/starship.nix
./programs/streamlink.nix
./programs/swayimg.nix
./programs/swaylock.nix
./programs/swayr.nix
./programs/taskwarrior.nix
./programs/tealdeer.nix
./programs/terminator.nix
./programs/termite.nix
./programs/tex-fmt.nix
./programs/texlive.nix
./programs/thefuck.nix
./programs/thunderbird.nix
./programs/timidity.nix
./programs/tint2.nix
./programs/tiny.nix
./programs/tmate.nix
./programs/tmux.nix
./programs/tofi.nix
./programs/todoman.nix
./programs/topgrade.nix
./programs/translate-shell.nix
./programs/urxvt.nix
./programs/vdirsyncer.nix
./programs/vifm.nix
./programs/vim-vint.nix
./programs/vim.nix
./programs/vinegar.nix
./programs/vscode.nix
./programs/vscode/haskell.nix
./programs/pywal.nix
./programs/rbenv.nix
./programs/watson.nix
./programs/waylogout.nix
./programs/waybar.nix
./programs/wezterm.nix
./programs/wlogout.nix
./programs/wofi.nix
./programs/xmobar.nix
./programs/xplr.nix
./programs/yambar.nix
./programs/yazi.nix
./programs/yt-dlp.nix
./programs/z-lua.nix
./programs/zathura.nix
./programs/zed-editor.nix
./programs/zellij.nix
./programs/zk.nix
./programs/zoxide.nix
./programs/zplug.nix
./programs/zsh.nix
./programs/zsh/prezto.nix
./programs/zsh/zsh-abbr.nix
./services/activitywatch.nix
./services/amberol.nix
./services/arrpc.nix
./services/autorandr.nix
./services/avizo.nix
./services/barrier.nix
./services/batsignal.nix
./services/betterlockscreen.nix
./services/blanket.nix
./services/blueman-applet.nix
./services/borgmatic.nix
./services/cachix-agent.nix
./services/caffeine.nix
./services/cbatticon.nix
./services/cliphist.nix
./services/clipman.nix
./services/clipmenu.nix
./services/clipse.nix
./services/comodoro.nix
./services/conky.nix
./services/copyq.nix
./services/darkman.nix
./services/davmail.nix
./services/devilspie2.nix
./services/dropbox.nix
./services/dunst.nix
./services/dwm-status.nix
./services/easyeffects.nix
./services/emacs.nix
./services/etesync-dav.nix
./services/espanso.nix
./services/flameshot.nix
./services/fluidsynth.nix
./services/fnott.nix
./services/fusuma.nix
./services/getmail.nix
./services/git-sync.nix
./services/glance.nix
./services/gnome-keyring.nix
./services/gpg-agent.nix
./services/grobi.nix
./services/gromit-mpx.nix
./services/home-manager-auto-expire.nix
./services/home-manager-auto-upgrade.nix
./services/hound.nix
./services/hypridle.nix
./services/hyprpaper.nix
./services/hyprpolkitagent.nix
./services/imapnotify.nix
./services/jankyborders.nix
./services/kanshi.nix
./services/kbfs.nix
./services/kdeconnect.nix
./services/keybase.nix
./services/keynav.nix
./services/librespot.nix
./services/lieer.nix
./services/linux-wallpaperengine.nix
./services/listenbrainz-mpd.nix
./services/lorri.nix
./services/lxqt-policykit-agent.nix
./services/macos-remap-keys
./services/mako.nix
./services/mbsync.nix
./services/megasync.nix
./services/mopidy.nix
./services/mpd.nix
./services/mpdris2.nix
./services/mpdscribble.nix
./services/mpd-discord-rpc.nix
./services/mpd-mpris.nix
./services/mpris-proxy.nix
./services/muchsync.nix
./services/network-manager-applet.nix
./services/nextcloud-client.nix
./services/nix-gc.nix
./services/notify-osd.nix
./services/ollama.nix
./services/opensnitch-ui.nix
./services/osmscout-server.nix
./services/owncloud-client.nix
./services/pantalaimon.nix
./services/parcellite.nix
./services/pass-secret-service.nix
./services/pasystray.nix
./services/pbgopy.nix
./services/picom.nix
./services/plan9port.nix
./services/playerctld.nix
./services/plex-mpv-shim.nix
./services/podman-linux
./services/polkit-gnome.nix
./services/polybar.nix
./services/poweralertd.nix
./services/psd.nix
./services/pueue.nix
./services/pulseeffects.nix
./services/random-background.nix
./services/recoll.nix
./services/redshift-gammastep/gammastep.nix
./services/redshift-gammastep/redshift.nix
./services/remmina.nix
./services/rsibreak.nix
./services/safeeyes.nix
./services/screen-locker.nix
./services/sctd.nix
./services/signaturepdf.nix
./services/skhd.nix
./services/snixembed.nix
./services/spotifyd.nix
./services/ssh-agent.nix
./services/stalonetray.nix
./services/status-notifier-watcher.nix
./services/swayidle.nix
./services/swaync.nix
./services/swayosd.nix
./services/swww.nix
./services/sxhkd.nix
./services/syncthing.nix
./services/systembus-notify.nix
./services/taffybar.nix
./services/tahoe-lafs.nix
./services/taskwarrior-sync.nix
./services/tldr-update.nix
./services/trayer.nix
./services/trayscale.nix
./services/twmn.nix
./services/udiskie.nix
./services/unclutter.nix
./services/unison.nix
./services/vdirsyncer.nix
./services/volnoti.nix
./services/window-managers/awesome.nix
./services/window-managers/bspwm/default.nix
./services/window-managers/fluxbox.nix
./services/window-managers/herbstluftwm.nix
./services/window-managers/hyprland.nix
./services/window-managers/i3-sway/i3.nix
./services/window-managers/i3-sway/sway.nix
./services/window-managers/i3-sway/swaynag.nix
./services/window-managers/river.nix
./services/window-managers/spectrwm.nix
./services/window-managers/wayfire.nix
./services/window-managers/xmonad.nix
./services/wlsunset.nix
./services/wluma.nix
./services/wob.nix
./services/wpaperd.nix
./services/xcape.nix
./services/xembed-sni-proxy.nix
./services/xidlehook.nix
./services/xscreensaver.nix
./services/xsettingsd.nix
./services/xsuspender.nix
./services/yubikey-agent.nix
./systemd.nix
./targets/darwin
./targets/generic-linux.nix
./wayland.nix
./xresources.nix
./xsession.nix
./misc/nix.nix
(pkgs.path + "/nixos/modules/misc/assertions.nix")
(pkgs.path + "/nixos/modules/misc/meta.nix")
modules =
[
./accounts/email.nix
./accounts/calendar.nix
./accounts/contacts.nix
./config/home-cursor.nix
./config/i18n.nix
./files.nix
./home-environment.nix
./i18n/input-method/default.nix
./launchd/default.nix
./manual.nix
./misc/dconf.nix
./misc/debug.nix
./misc/editorconfig.nix
./misc/fontconfig.nix
./misc/gtk.nix
./misc/lib.nix
./misc/mozilla-messaging-hosts.nix
./misc/news.nix
./misc/nixgl.nix
./misc/numlock.nix
./misc/pam.nix
./misc/qt.nix
./misc/qt/kconfig.nix
./misc/shell.nix
./misc/specialisation.nix
./misc/submodule-support.nix
./misc/tmpfiles.nix
./misc/uninstall.nix
./misc/version.nix
./misc/vte.nix
./misc/xdg-autostart.nix
./misc/xdg-desktop-entries.nix
./misc/xdg-mime-apps.nix
./misc/xdg-mime.nix
./misc/xdg-portal.nix
./misc/xdg-system-dirs.nix
./misc/xdg-user-dirs.nix
./misc/xdg.nix
./misc/xfconf.nix
./programs/abook.nix
./programs/aerc.nix
./programs/aerospace.nix
./programs/afew.nix
./programs/alacritty.nix
./programs/alot.nix
./programs/antidote.nix
./programs/aria2.nix
./programs/astroid.nix
./programs/atuin.nix
./programs/autojump.nix
./programs/autorandr.nix
./programs/awscli.nix
./programs/bash.nix
./programs/bashmount.nix
./programs/bat.nix
./programs/bacon.nix
./programs/beets.nix
./programs/bemenu.nix
./programs/borgmatic.nix
./programs/bottom.nix
./programs/boxxy.nix
./programs/broot.nix
./programs/browserpass.nix
./programs/btop.nix
./programs/bun.nix
./programs/carapace.nix
./programs/cava.nix
./programs/cavalier.nix
./programs/chromium.nix
./programs/cmus.nix
./programs/command-not-found/command-not-found.nix
./programs/comodoro.nix
./programs/darcs.nix
./programs/dircolors.nix
./programs/direnv.nix
./programs/discocss.nix
./programs/distrobox.nix
./programs/earthly.nix
./programs/eclipse.nix
./programs/emacs.nix
./programs/eww.nix
./programs/eza.nix
./programs/fastfetch.nix
./programs/fd.nix
./programs/feh.nix
./programs/firefox.nix
./programs/fish.nix
./programs/floorp.nix
./programs/foot.nix
./programs/freetube.nix
./programs/fuzzel.nix
./programs/fzf.nix
./programs/gallery-dl.nix
./programs/getmail.nix
./programs/gh.nix
./programs/gh-dash.nix
./programs/ghostty.nix
./programs/git-cliff.nix
./programs/git-credential-oauth.nix
./programs/git-worktree-switcher.nix
./programs/git.nix
./programs/gitui.nix
./programs/gnome-shell.nix
./programs/gnome-terminal.nix
./programs/go.nix
./programs/gpg.nix
./programs/gradle.nix
./programs/granted.nix
./programs/havoc.nix
./programs/helix.nix
./programs/hexchat.nix
./programs/himalaya.nix
./programs/home-manager.nix
./programs/hstr.nix
./programs/htop.nix
./programs/hyfetch.nix
./programs/hyprlock.nix
./programs/i3blocks.nix
./programs/i3status-rust.nix
./programs/i3status.nix
./programs/iamb.nix
./programs/imv.nix
./programs/info.nix
./programs/ion.nix
./programs/irssi.nix
./programs/java.nix
./programs/jetbrains-remote.nix
./programs/jq.nix
./programs/jqp.nix
./programs/jujutsu.nix
./programs/joshuto.nix
./programs/joplin-desktop.nix
./programs/just.nix
./programs/k9s.nix
./programs/kakoune.nix
./programs/keychain.nix
./programs/khal.nix
./programs/khard.nix
./programs/kitty.nix
./programs/kodi.nix
./programs/kubecolor.nix
./programs/lapce.nix
./programs/lazydocker.nix
./programs/lazygit.nix
./programs/ledger.nix
./programs/less.nix
./programs/lesspipe.nix
./programs/lf.nix
./programs/librewolf.nix
./programs/lieer.nix
./programs/looking-glass-client.nix
./programs/lsd.nix
./programs/man.nix
./programs/mangohud.nix
./programs/matplotlib.nix
./programs/mbsync.nix
./programs/mcfly.nix
./programs/mercurial.nix
./programs/mergiraf.nix
./programs/micro.nix
./programs/mise.nix
./programs/mods.nix
./programs/mpv.nix
./programs/mr.nix
./programs/msmtp.nix
./programs/mu.nix
./programs/mujmap.nix
./programs/navi.nix
./programs/ncmpcpp.nix
./programs/ncspot.nix
./programs/ne.nix
./programs/neomutt.nix
./programs/neovide.nix
./programs/neovim.nix
./programs/newsboat.nix
./programs/nh.nix
./programs/nheko.nix
./programs/nix-index.nix
./programs/nix-your-shell.nix
./programs/nnn.nix
./programs/noti.nix
./programs/notmuch.nix
./programs/nushell.nix
./programs/obs-studio.nix
./programs/octant.nix
./programs/offlineimap.nix
./programs/oh-my-posh.nix
./programs/onlyoffice.nix
./programs/opam.nix
./programs/openstackclient.nix
./programs/pandoc.nix
./programs/papis.nix
./programs/password-store.nix
./programs/pay-respects.nix
./programs/pazi.nix
./programs/pet.nix
./programs/pidgin.nix
./programs/pistol.nix
./programs/piston-cli.nix
./programs/pls.nix
./programs/poetry.nix
./programs/powerline-go.nix
./programs/pqiv.nix
./programs/pubs.nix
./programs/pyenv.nix
./programs/pylint.nix
./programs/qcal.nix
./programs/qutebrowser.nix
./programs/ranger.nix
./programs/rbw.nix
./programs/rclone.nix
./programs/readline.nix
./programs/rio.nix
./programs/ripgrep.nix
./programs/ripgrep-all.nix
./programs/rofi-pass.nix
./programs/rofi.nix
./programs/rtorrent.nix
./programs/ruff.nix
./programs/sagemath.nix
./programs/sapling.nix
./programs/sbt.nix
./programs/scmpuff.nix
./programs/script-directory.nix
./programs/senpai.nix
./programs/sesh.nix
./programs/sftpman.nix
./programs/sioyek.nix
./programs/skim.nix
./programs/sm64ex.nix
./programs/smug.nix
./programs/spotify-player.nix
./programs/sqls.nix
./programs/ssh.nix
./programs/starship.nix
./programs/streamlink.nix
./programs/swayimg.nix
./programs/swaylock.nix
./programs/swayr.nix
./programs/taskwarrior.nix
./programs/tealdeer.nix
./programs/terminator.nix
./programs/termite.nix
./programs/tex-fmt.nix
./programs/texlive.nix
./programs/thefuck.nix
./programs/thunderbird.nix
./programs/timidity.nix
./programs/tint2.nix
./programs/tiny.nix
./programs/tmate.nix
./programs/tmux.nix
./programs/tofi.nix
./programs/todoman.nix
./programs/topgrade.nix
./programs/translate-shell.nix
./programs/urxvt.nix
./programs/vdirsyncer.nix
./programs/vifm.nix
./programs/vim-vint.nix
./programs/vim.nix
./programs/vinegar.nix
./programs/vscode.nix
./programs/vscode/haskell.nix
./programs/pywal.nix
./programs/rbenv.nix
./programs/watson.nix
./programs/waylogout.nix
./programs/waybar.nix
./programs/wezterm.nix
./programs/wlogout.nix
./programs/wofi.nix
./programs/xmobar.nix
./programs/xplr.nix
./programs/yambar.nix
./programs/yazi.nix
./programs/yt-dlp.nix
./programs/z-lua.nix
./programs/zathura.nix
./programs/zed-editor.nix
./programs/zellij.nix
./programs/zk.nix
./programs/zoxide.nix
./programs/zplug.nix
./programs/zsh.nix
./programs/zsh/prezto.nix
./programs/zsh/zsh-abbr.nix
./services/activitywatch.nix
./services/amberol.nix
./services/arrpc.nix
./services/autorandr.nix
./services/avizo.nix
./services/barrier.nix
./services/batsignal.nix
./services/betterlockscreen.nix
./services/blanket.nix
./services/blueman-applet.nix
./services/borgmatic.nix
./services/cachix-agent.nix
./services/caffeine.nix
./services/cbatticon.nix
./services/cliphist.nix
./services/clipman.nix
./services/clipmenu.nix
./services/clipse.nix
./services/comodoro.nix
./services/conky.nix
./services/copyq.nix
./services/darkman.nix
./services/davmail.nix
./services/devilspie2.nix
./services/dropbox.nix
./services/dunst.nix
./services/dwm-status.nix
./services/easyeffects.nix
./services/emacs.nix
./services/etesync-dav.nix
./services/espanso.nix
./services/flameshot.nix
./services/fluidsynth.nix
./services/fnott.nix
./services/fusuma.nix
./services/getmail.nix
./services/git-sync.nix
./services/glance.nix
./services/gnome-keyring.nix
./services/gpg-agent.nix
./services/grobi.nix
./services/gromit-mpx.nix
./services/home-manager-auto-expire.nix
./services/home-manager-auto-upgrade.nix
./services/hound.nix
./services/hypridle.nix
./services/hyprpaper.nix
./services/hyprpolkitagent.nix
./services/imapnotify.nix
./services/jankyborders.nix
./services/kanshi.nix
./services/kbfs.nix
./services/kdeconnect.nix
./services/keybase.nix
./services/keynav.nix
./services/librespot.nix
./services/lieer.nix
./services/linux-wallpaperengine.nix
./services/listenbrainz-mpd.nix
./services/lorri.nix
./services/lxqt-policykit-agent.nix
./services/macos-remap-keys
./services/mako.nix
./services/mbsync.nix
./services/megasync.nix
./services/mopidy.nix
./services/mpd.nix
./services/mpdris2.nix
./services/mpdscribble.nix
./services/mpd-discord-rpc.nix
./services/mpd-mpris.nix
./services/mpris-proxy.nix
./services/muchsync.nix
./services/network-manager-applet.nix
./services/nextcloud-client.nix
./services/nix-gc.nix
./services/notify-osd.nix
./services/ollama.nix
./services/opensnitch-ui.nix
./services/osmscout-server.nix
./services/owncloud-client.nix
./services/pantalaimon.nix
./services/parcellite.nix
./services/pass-secret-service.nix
./services/pasystray.nix
./services/pbgopy.nix
./services/picom.nix
./services/plan9port.nix
./services/playerctld.nix
./services/plex-mpv-shim.nix
./services/podman-linux
./services/polkit-gnome.nix
./services/polybar.nix
./services/poweralertd.nix
./services/psd.nix
./services/pueue.nix
./services/pulseeffects.nix
./services/random-background.nix
./services/recoll.nix
./services/redshift-gammastep/gammastep.nix
./services/redshift-gammastep/redshift.nix
./services/remmina.nix
./services/rsibreak.nix
./services/safeeyes.nix
./services/screen-locker.nix
./services/sctd.nix
./services/signaturepdf.nix
./services/skhd.nix
./services/snixembed.nix
./services/spotifyd.nix
./services/ssh-agent.nix
./services/stalonetray.nix
./services/status-notifier-watcher.nix
./services/swayidle.nix
./services/swaync.nix
./services/swayosd.nix
./services/swww.nix
./services/sxhkd.nix
./services/syncthing.nix
./services/systembus-notify.nix
./services/taffybar.nix
./services/tahoe-lafs.nix
./services/taskwarrior-sync.nix
./services/tldr-update.nix
./services/trayer.nix
./services/trayscale.nix
./services/twmn.nix
./services/udiskie.nix
./services/unclutter.nix
./services/unison.nix
./services/vdirsyncer.nix
./services/volnoti.nix
./services/window-managers/awesome.nix
./services/window-managers/bspwm/default.nix
./services/window-managers/fluxbox.nix
./services/window-managers/herbstluftwm.nix
./services/window-managers/hyprland.nix
./services/window-managers/i3-sway/i3.nix
./services/window-managers/i3-sway/sway.nix
./services/window-managers/i3-sway/swaynag.nix
./services/window-managers/river.nix
./services/window-managers/spectrwm.nix
./services/window-managers/wayfire.nix
./services/window-managers/xmonad.nix
./services/wlsunset.nix
./services/wluma.nix
./services/wob.nix
./services/wpaperd.nix
./services/xcape.nix
./services/xembed-sni-proxy.nix
./services/xidlehook.nix
./services/xscreensaver.nix
./services/xsettingsd.nix
./services/xsuspender.nix
./services/yubikey-agent.nix
./systemd.nix
./targets/darwin
./targets/generic-linux.nix
./wayland.nix
./xresources.nix
./xsession.nix
./misc/nix.nix
(pkgs.path + "/nixos/modules/misc/assertions.nix")
(pkgs.path + "/nixos/modules/misc/meta.nix")
(lib.mkRemovedOptionModule [ "services" "password-store-sync" ] ''
Use services.git-sync instead.
'')
(lib.mkRemovedOptionModule [ "services" "keepassx" ] ''
KeePassX is no longer maintained.
'')
] ++ lib.optional useNixpkgsModule ./misc/nixpkgs.nix
(lib.mkRemovedOptionModule [ "services" "password-store-sync" ] ''
Use services.git-sync instead.
'')
(lib.mkRemovedOptionModule [ "services" "keepassx" ] ''
KeePassX is no longer maintained.
'')
]
++ lib.optional useNixpkgsModule ./misc/nixpkgs.nix
++ lib.optional (!useNixpkgsModule) ./misc/nixpkgs-disabled.nix;
pkgsModule = { config, ... }: {
config = {
_module.args.baseModules = modules;
_module.args.pkgsPath = lib.mkDefault
(if lib.versionAtLeast config.home.stateVersion "20.09" then
pkgs.path
else
<nixpkgs>);
_module.args.pkgs = lib.mkDefault pkgs;
_module.check = check;
lib = lib.hm;
} // lib.optionalAttrs useNixpkgsModule {
nixpkgs.system = lib.mkDefault pkgs.stdenv.hostPlatform.system;
pkgsModule =
{ config, ... }:
{
config =
{
_module.args.baseModules = modules;
_module.args.pkgsPath = lib.mkDefault (
if lib.versionAtLeast config.home.stateVersion "20.09" then pkgs.path else <nixpkgs>
);
_module.args.pkgs = lib.mkDefault pkgs;
_module.check = check;
lib = lib.hm;
}
// lib.optionalAttrs useNixpkgsModule {
nixpkgs.system = lib.mkDefault pkgs.stdenv.hostPlatform.system;
};
};
};
in modules ++ [ pkgsModule ]
in
modules ++ [ pkgsModule ]

View file

@ -1,6 +1,13 @@
{ config, lib, pkgs, ... }:
let cfg = config.programs.abook;
in {
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.abook;
in
{
options.programs.abook = {
enable = lib.mkEnableOption "Abook";

View file

@ -1,17 +1,29 @@
{ config, lib, confSections, confSection, ... }:
{
config,
lib,
confSections,
confSection,
...
}:
let
inherit (lib) literalExpression mkOption types;
mapAttrNames = f: attr:
lib.listToAttrs (lib.attrValues (lib.mapAttrs (k: v: {
name = f k;
value = v;
}) attr));
mapAttrNames =
f: attr:
lib.listToAttrs (
lib.attrValues (
lib.mapAttrs (k: v: {
name = f k;
value = v;
}) attr
)
);
addAccountName = name: k: "${k}:account=${name}";
oauth2Params = mkOption {
type = with types;
type =
with types;
nullOr (submodule {
options = {
token_endpoint = mkOption {
@ -37,7 +49,9 @@ let
};
});
default = null;
example = { token_endpoint = "<token_endpoint>"; };
example = {
token_endpoint = "<token_endpoint>";
};
description = ''
Sets the oauth2 params if authentication mechanism oauthbearer or
xoauth2 is used.
@ -45,157 +59,188 @@ let
'';
};
in {
in
{
type = mkOption {
type = types.attrsOf (types.submodule {
options.aerc = {
enable = lib.mkEnableOption "aerc";
extraAccounts = mkOption {
type = confSection;
default = { };
example =
literalExpression ''{ source = "maildir://~/Maildir/example"; }'';
description = ''
Extra config added to the configuration section for this account in
{file}`$HOME/.config/aerc/accounts.conf`.
See {manpage}`aerc-accounts(5)`.
'';
type = types.attrsOf (
types.submodule {
options.aerc = {
enable = lib.mkEnableOption "aerc";
extraAccounts = mkOption {
type = confSection;
default = { };
example = literalExpression ''{ source = "maildir://~/Maildir/example"; }'';
description = ''
Extra config added to the configuration section for this account in
{file}`$HOME/.config/aerc/accounts.conf`.
See {manpage}`aerc-accounts(5)`.
'';
};
extraBinds = mkOption {
type = confSections;
default = { };
example = literalExpression ''{ messages = { d = ":move ''${folder.trash}<Enter>"; }; }'';
description = ''
Extra bindings specific to this account, added to
{file}`$HOME/.config/aerc/binds.conf`.
See {manpage}`aerc-binds(5)`.
'';
};
extraConfig = mkOption {
type = confSections;
default = { };
example = literalExpression "{ ui = { sidebar-width = 25; }; }";
description = ''
Config specific to this account, added to {file}`$HOME/.config/aerc/aerc.conf`.
Aerc only supports per-account UI configuration.
For other sections of {file}`$HOME/.config/aerc/aerc.conf`,
use `programs.aerc.extraConfig`.
See {manpage}`aerc-config(5)`.
'';
};
imapAuth = mkOption {
type =
with types;
nullOr (enum [
"oauthbearer"
"xoauth2"
]);
default = null;
example = "auth";
description = ''
Sets the authentication mechanism if imap is used as the incoming
method.
See {manpage}`aerc-imap(5)`.
'';
};
imapOauth2Params = oauth2Params;
smtpAuth = mkOption {
type =
with types;
nullOr (enum [
"none"
"plain"
"login"
"oauthbearer"
"xoauth2"
]);
default = "plain";
example = "auth";
description = ''
Sets the authentication mechanism if smtp is used as the outgoing
method.
See {manpage}`aerc-smtp(5)`.
'';
};
smtpOauth2Params = oauth2Params;
};
extraBinds = mkOption {
type = confSections;
default = { };
example = literalExpression
''{ messages = { d = ":move ''${folder.trash}<Enter>"; }; }'';
description = ''
Extra bindings specific to this account, added to
{file}`$HOME/.config/aerc/binds.conf`.
See {manpage}`aerc-binds(5)`.
'';
};
extraConfig = mkOption {
type = confSections;
default = { };
example = literalExpression "{ ui = { sidebar-width = 25; }; }";
description = ''
Config specific to this account, added to {file}`$HOME/.config/aerc/aerc.conf`.
Aerc only supports per-account UI configuration.
For other sections of {file}`$HOME/.config/aerc/aerc.conf`,
use `programs.aerc.extraConfig`.
See {manpage}`aerc-config(5)`.
'';
};
imapAuth = mkOption {
type = with types; nullOr (enum [ "oauthbearer" "xoauth2" ]);
default = null;
example = "auth";
description = ''
Sets the authentication mechanism if imap is used as the incoming
method.
See {manpage}`aerc-imap(5)`.
'';
};
imapOauth2Params = oauth2Params;
smtpAuth = mkOption {
type = with types;
nullOr (enum [ "none" "plain" "login" "oauthbearer" "xoauth2" ]);
default = "plain";
example = "auth";
description = ''
Sets the authentication mechanism if smtp is used as the outgoing
method.
See {manpage}`aerc-smtp(5)`.
'';
};
smtpOauth2Params = oauth2Params;
};
});
}
);
};
mkAccount = name: account:
mkAccount =
name: account:
let
nullOrMap = f: v: if v == null then v else f v;
optPort = port: if port != null then ":${toString port}" else "";
optAttr = k: v:
if v != null && v != [ ] && v != "" then { ${k} = v; } else { };
optAttr = k: v: if v != null && v != [ ] && v != "" then { ${k} = v; } else { };
optPwCmd = k: p:
optAttr "${k}-cred-cmd" (nullOrMap (lib.concatStringsSep " ") p);
optPwCmd = k: p: optAttr "${k}-cred-cmd" (nullOrMap (lib.concatStringsSep " ") p);
useOauth = auth: builtins.elem auth [ "oauthbearer" "xoauth2" ];
useOauth =
auth:
builtins.elem auth [
"oauthbearer"
"xoauth2"
];
oauthParams = { auth, params }:
oauthParams =
{ auth, params }:
if useOauth auth && params != null && params != { } then
"?" + builtins.concatStringsSep "&"
(lib.attrsets.mapAttrsToList (k: v: k + "=" + lib.strings.escapeURL v)
(lib.attrsets.filterAttrs (k: v: v != null) params))
"?"
+ builtins.concatStringsSep "&" (
lib.attrsets.mapAttrsToList (k: v: k + "=" + lib.strings.escapeURL v) (
lib.attrsets.filterAttrs (k: v: v != null) params
)
)
else
"";
mkConfig = {
maildir = cfg: {
source =
"maildir://${config.accounts.email.maildirBasePath}/${cfg.maildir.path}";
source = "maildir://${config.accounts.email.maildirBasePath}/${cfg.maildir.path}";
};
maildirpp = cfg: {
source =
"maildirpp://${config.accounts.email.maildirBasePath}/${cfg.maildir.path}/Inbox";
source = "maildirpp://${config.accounts.email.maildirBasePath}/${cfg.maildir.path}/Inbox";
};
imap = { userName, imap, passwordCommand, aerc, ... }@cfg:
imap =
{
userName,
imap,
passwordCommand,
aerc,
...
}@cfg:
let
loginMethod' =
if cfg.aerc.imapAuth != null then "+${cfg.aerc.imapAuth}" else "";
loginMethod' = if cfg.aerc.imapAuth != null then "+${cfg.aerc.imapAuth}" else "";
oauthParams' = oauthParams {
auth = cfg.aerc.imapAuth;
params = cfg.aerc.imapOauth2Params;
};
protocol = if imap.tls.enable then
if imap.tls.useStartTls then "imap" else "imaps${loginMethod'}"
else
"imap+insecure";
protocol =
if imap.tls.enable then
if imap.tls.useStartTls then "imap" else "imaps${loginMethod'}"
else
"imap+insecure";
port' = optPort imap.port;
in {
source =
"${protocol}://${userName}@${imap.host}${port'}${oauthParams'}";
} // optPwCmd "source" passwordCommand;
in
{
source = "${protocol}://${userName}@${imap.host}${port'}${oauthParams'}";
}
// optPwCmd "source" passwordCommand;
smtp = { userName, smtp, passwordCommand, ... }@cfg:
smtp =
{
userName,
smtp,
passwordCommand,
...
}@cfg:
let
loginMethod' =
if cfg.aerc.smtpAuth != null then "+${cfg.aerc.smtpAuth}" else "";
loginMethod' = if cfg.aerc.smtpAuth != null then "+${cfg.aerc.smtpAuth}" else "";
oauthParams' = oauthParams {
auth = cfg.aerc.smtpAuth;
params = cfg.aerc.smtpOauth2Params;
};
protocol = if smtp.tls.enable then
if smtp.tls.useStartTls then
"smtp${loginMethod'}"
protocol =
if smtp.tls.enable then
if smtp.tls.useStartTls then "smtp${loginMethod'}" else "smtps${loginMethod'}"
else
"smtps${loginMethod'}"
else
"smtp+insecure${loginMethod'}";
"smtp+insecure${loginMethod'}";
port' = optPort smtp.port;
in {
outgoing =
"${protocol}://${userName}@${smtp.host}${port'}${oauthParams'}";
} // optPwCmd "outgoing" passwordCommand;
in
{
outgoing = "${protocol}://${userName}@${smtp.host}${port'}${oauthParams'}";
}
// optPwCmd "outgoing" passwordCommand;
msmtp = cfg: {
outgoing = "msmtpq --read-envelope-from --read-recipients";
@ -203,17 +248,21 @@ in {
};
basicCfg = account:
basicCfg =
account:
{
from = "${account.realName} <${account.address}>";
} // (optAttr "copy-to" account.folders.sent)
}
// (optAttr "copy-to" account.folders.sent)
// (optAttr "default" account.folders.inbox)
// (optAttr "postpone" account.folders.drafts)
// (optAttr "aliases" account.aliases);
sourceCfg = account:
if account.mbsync.enable && account.mbsync.flatten == null
&& account.mbsync.subFolders == "Maildir++" then
sourceCfg =
account:
if
account.mbsync.enable && account.mbsync.flatten == null && account.mbsync.subFolders == "Maildir++"
then
mkConfig.maildirpp account
else if account.mbsync.enable || account.offlineimap.enable then
mkConfig.maildir account
@ -222,7 +271,8 @@ in {
else
{ };
outgoingCfg = account:
outgoingCfg =
account:
if account.msmtp.enable then
mkConfig.msmtp account
else if account.smtp != null then
@ -230,19 +280,22 @@ in {
else
{ };
gpgCfg = account:
gpgCfg =
account:
lib.optionalAttrs (account.gpg != null) {
pgp-key-id = account.gpg.key;
pgp-auto-sign = account.gpg.signByDefault;
pgp-opportunistic-encrypt = account.gpg.encryptByDefault;
};
in (basicCfg account) // (sourceCfg account) // (outgoingCfg account)
// (gpgCfg account) // account.aerc.extraAccounts;
in
(basicCfg account)
// (sourceCfg account)
// (outgoingCfg account)
// (gpgCfg account)
// account.aerc.extraAccounts;
mkAccountConfig = name: account:
mapAttrNames (addAccountName name) account.aerc.extraConfig;
mkAccountConfig = name: account: mapAttrNames (addAccountName name) account.aerc.extraConfig;
mkAccountBinds = name: account:
mapAttrNames (addAccountName name) account.aerc.extraBinds;
mkAccountBinds = name: account: mapAttrNames (addAccountName name) account.aerc.extraBinds;
}

View file

@ -1,15 +1,34 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
attrsets generators literalExpression mapAttrs mkIf mkOption types;
attrsets
generators
literalExpression
mapAttrs
mkIf
mkOption
types
;
cfg = config.programs.aerc;
primitive = with types;
((type: either type (listOf type)) (nullOr (oneOf [ str int bool float ])))
primitive =
with types;
((type: either type (listOf type)) (
nullOr (oneOf [
str
int
bool
float
])
))
// {
description =
"values (null, bool, int, string, or float) or a list of values, that will be joined with a comma";
description = "values (null, bool, int, string, or float) or a list of values, that will be joined with a comma";
};
confSection = types.attrsOf primitive;
@ -19,18 +38,25 @@ let
sectionsOrLines = types.either types.lines confSections;
accounts = import ./aerc-accounts.nix {
inherit config pkgs lib confSection confSections;
inherit
config
pkgs
lib
confSection
confSections
;
};
aerc-accounts =
attrsets.filterAttrs (_: v: v.aerc.enable) config.accounts.email.accounts;
aerc-accounts = attrsets.filterAttrs (_: v: v.aerc.enable) config.accounts.email.accounts;
configDir = if (pkgs.stdenv.isDarwin && !config.xdg.enable) then
"Library/Preferences/aerc"
else
"${config.xdg.configHome}/aerc";
configDir =
if (pkgs.stdenv.isDarwin && !config.xdg.enable) then
"Library/Preferences/aerc"
else
"${config.xdg.configHome}/aerc";
in {
in
{
meta.maintainers = with lib.hm.maintainers; [ lukasngl ];
options.accounts.email.accounts = accounts.type;
@ -44,8 +70,7 @@ in {
extraAccounts = mkOption {
type = sectionsOrLines;
default = { };
example = literalExpression
''{ Work = { source = "maildir://~/Maildir/work"; }; }'';
example = literalExpression ''{ Work = { source = "maildir://~/Maildir/work"; }; }'';
description = ''
Extra lines added to {file}`$HOME/.config/aerc/accounts.conf`.
@ -103,127 +128,151 @@ in {
};
};
config = let
joinCfg = cfgs: lib.concatStringsSep "\n" (lib.filter (v: v != "") cfgs);
config =
let
joinCfg = cfgs: lib.concatStringsSep "\n" (lib.filter (v: v != "") cfgs);
toINI = conf: # quirk: global section is prepended w/o section heading
let
global = conf.global or { };
local = removeAttrs conf [ "global" ];
mkValueString = v:
if lib.isList v then # join with comma
lib.concatStringsSep ","
(map (generators.mkValueStringDefault { }) v)
else
generators.mkValueStringDefault { } v;
mkKeyValue =
generators.mkKeyValueDefault { inherit mkValueString; } " = ";
in joinCfg [
(generators.toKeyValue { inherit mkKeyValue; } global)
(generators.toINI { inherit mkKeyValue; } local)
toINI =
conf: # quirk: global section is prepended w/o section heading
let
global = conf.global or { };
local = removeAttrs conf [ "global" ];
mkValueString =
v:
if lib.isList v then # join with comma
lib.concatStringsSep "," (map (generators.mkValueStringDefault { }) v)
else
generators.mkValueStringDefault { } v;
mkKeyValue = generators.mkKeyValueDefault { inherit mkValueString; } " = ";
in
joinCfg [
(generators.toKeyValue { inherit mkKeyValue; } global)
(generators.toINI { inherit mkKeyValue; } local)
];
mkINI = conf: if lib.isString conf then conf else toINI conf;
mkStyleset = attrsets.mapAttrs' (
k: v:
let
value = if lib.isString v then v else toINI { global = v; };
in
{
name = "${configDir}/stylesets/${k}";
value.text = joinCfg [
header
value
];
}
);
mkTemplates = attrsets.mapAttrs' (
k: v: {
name = "${configDir}/templates/${k}";
value.text = v;
}
);
primaryAccount = attrsets.filterAttrs (_: v: v.primary) aerc-accounts;
otherAccounts = attrsets.filterAttrs (_: v: !v.primary) aerc-accounts;
primaryAccountAccounts = mapAttrs accounts.mkAccount primaryAccount;
accountsExtraAccounts = mapAttrs accounts.mkAccount otherAccounts;
accountsExtraConfig = mapAttrs accounts.mkAccountConfig aerc-accounts;
accountsExtraBinds = mapAttrs accounts.mkAccountBinds aerc-accounts;
joinContextual = contextual: joinCfg (map mkINI (lib.attrValues contextual));
isRecursivelyEmpty =
x:
if lib.isAttrs x then lib.all (x: x == { } || isRecursivelyEmpty x) (lib.attrValues x) else false;
genAccountsConf = (
(cfg.extraAccounts != "" && cfg.extraAccounts != { })
|| !(isRecursivelyEmpty accountsExtraAccounts)
|| !(isRecursivelyEmpty primaryAccountAccounts)
);
genAercConf = (
(cfg.extraConfig != "" && cfg.extraConfig != { }) || !(isRecursivelyEmpty accountsExtraConfig)
);
genBindsConf = (
(cfg.extraBinds != "" && cfg.extraBinds != { }) || !(isRecursivelyEmpty accountsExtraBinds)
);
header = ''
# Generated by Home Manager.
'';
in
mkIf cfg.enable {
warnings =
if genAccountsConf && (cfg.extraConfig.general.unsafe-accounts-conf or false) == false then
[
''
aerc: `programs.aerc.enable` is set, but `...extraConfig.general.unsafe-accounts-conf` is set to false or unset.
This will prevent aerc from starting; see `unsafe-accounts-conf` in the man page aerc-config(5):
> By default, the file permissions of accounts.conf must be restrictive and only allow reading by the file owner (0600).
> Set this option to true to ignore this permission check. Use this with care as it may expose your credentials.
These permissions are not possible with home-manager, since the generated file is in the nix-store (permissions 0444).
Therefore, please set `programs.aerc.extraConfig.general.unsafe-accounts-conf = true`.
This option is safe; if `passwordCommand` is properly set, no credentials will be written to the nix store.
''
]
else
[ ];
assertions = [
{
assertion =
let
extraConfigSections = (
lib.unique (lib.flatten (lib.mapAttrsToList (_: v: lib.attrNames v.aerc.extraConfig) aerc-accounts))
);
in
extraConfigSections == [ ] || extraConfigSections == [ "ui" ];
message = ''
Only the ui section of $XDG_CONFIG_HOME/aerc.conf supports contextual (per-account) configuration.
Please configure it with accounts.email.accounts._.aerc.extraConfig.ui and move any other
configuration to programs.aerc.extraConfig.
'';
}
];
mkINI = conf: if lib.isString conf then conf else toINI conf;
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
mkStyleset = attrsets.mapAttrs' (k: v:
let value = if lib.isString v then v else toINI { global = v; };
in {
name = "${configDir}/stylesets/${k}";
value.text = joinCfg [ header value ];
});
home.file =
{
"${configDir}/accounts.conf" = mkIf genAccountsConf {
text = joinCfg [
header
(mkINI cfg.extraAccounts)
(mkINI primaryAccountAccounts)
(mkINI accountsExtraAccounts)
];
};
mkTemplates = attrsets.mapAttrs' (k: v: {
name = "${configDir}/templates/${k}";
value.text = v;
});
"${configDir}/aerc.conf" = mkIf genAercConf {
text = joinCfg [
header
(mkINI cfg.extraConfig)
(joinContextual accountsExtraConfig)
];
};
primaryAccount = attrsets.filterAttrs (_: v: v.primary) aerc-accounts;
otherAccounts = attrsets.filterAttrs (_: v: !v.primary) aerc-accounts;
primaryAccountAccounts = mapAttrs accounts.mkAccount primaryAccount;
accountsExtraAccounts = mapAttrs accounts.mkAccount otherAccounts;
accountsExtraConfig = mapAttrs accounts.mkAccountConfig aerc-accounts;
accountsExtraBinds = mapAttrs accounts.mkAccountBinds aerc-accounts;
joinContextual = contextual:
joinCfg (map mkINI (lib.attrValues contextual));
isRecursivelyEmpty = x:
if lib.isAttrs x then
lib.all (x: x == { } || isRecursivelyEmpty x) (lib.attrValues x)
else
false;
genAccountsConf = ((cfg.extraAccounts != "" && cfg.extraAccounts != { })
|| !(isRecursivelyEmpty accountsExtraAccounts)
|| !(isRecursivelyEmpty primaryAccountAccounts));
genAercConf = ((cfg.extraConfig != "" && cfg.extraConfig != { })
|| !(isRecursivelyEmpty accountsExtraConfig));
genBindsConf = ((cfg.extraBinds != "" && cfg.extraBinds != { })
|| !(isRecursivelyEmpty accountsExtraBinds));
header = ''
# Generated by Home Manager.
'';
in mkIf cfg.enable {
warnings = if genAccountsConf
&& (cfg.extraConfig.general.unsafe-accounts-conf or false) == false then [''
aerc: `programs.aerc.enable` is set, but `...extraConfig.general.unsafe-accounts-conf` is set to false or unset.
This will prevent aerc from starting; see `unsafe-accounts-conf` in the man page aerc-config(5):
> By default, the file permissions of accounts.conf must be restrictive and only allow reading by the file owner (0600).
> Set this option to true to ignore this permission check. Use this with care as it may expose your credentials.
These permissions are not possible with home-manager, since the generated file is in the nix-store (permissions 0444).
Therefore, please set `programs.aerc.extraConfig.general.unsafe-accounts-conf = true`.
This option is safe; if `passwordCommand` is properly set, no credentials will be written to the nix store.
''] else
[ ];
assertions = [{
assertion = let
extraConfigSections = (lib.unique (lib.flatten
(lib.mapAttrsToList (_: v: lib.attrNames v.aerc.extraConfig)
aerc-accounts)));
in extraConfigSections == [ ] || extraConfigSections == [ "ui" ];
message = ''
Only the ui section of $XDG_CONFIG_HOME/aerc.conf supports contextual (per-account) configuration.
Please configure it with accounts.email.accounts._.aerc.extraConfig.ui and move any other
configuration to programs.aerc.extraConfig.
'';
}];
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
home.file = {
"${configDir}/accounts.conf" = mkIf genAccountsConf {
text = joinCfg [
header
(mkINI cfg.extraAccounts)
(mkINI primaryAccountAccounts)
(mkINI accountsExtraAccounts)
];
};
"${configDir}/aerc.conf" = mkIf genAercConf {
text = joinCfg [
header
(mkINI cfg.extraConfig)
(joinContextual accountsExtraConfig)
];
};
"${configDir}/binds.conf" = mkIf genBindsConf {
text = joinCfg [
header
(mkINI cfg.extraBinds)
(joinContextual accountsExtraBinds)
];
};
} // (mkStyleset cfg.stylesets) // (mkTemplates cfg.templates);
};
"${configDir}/binds.conf" = mkIf genBindsConf {
text = joinCfg [
header
(mkINI cfg.extraBinds)
(joinContextual accountsExtraBinds)
];
};
}
// (mkStyleset cfg.stylesets)
// (mkTemplates cfg.templates);
};
}

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkOption types;
cfg = config.programs.aerospace;
@ -6,22 +11,29 @@ let
tomlFormat = pkgs.formats.toml { };
# filterAttrsRecursive supporting lists, as well.
filterListAndAttrsRecursive = pred: set:
lib.listToAttrs (lib.concatMap (name:
let v = set.${name};
in if pred v then
[
(lib.nameValuePair name (if lib.isAttrs v then
filterListAndAttrsRecursive pred v
else if lib.isList v then
(map (i:
if lib.isAttrs i then filterListAndAttrsRecursive pred i else i)
(lib.filter pred v))
else
v))
]
else
[ ]) (lib.attrNames set));
filterListAndAttrsRecursive =
pred: set:
lib.listToAttrs (
lib.concatMap (
name:
let
v = set.${name};
in
if pred v then
[
(lib.nameValuePair name (
if lib.isAttrs v then
filterListAndAttrsRecursive pred v
else if lib.isList v then
(map (i: if lib.isAttrs i then filterListAndAttrsRecursive pred i else i) (lib.filter pred v))
else
v
))
]
else
[ ]
) (lib.attrNames set)
);
filterNulls = filterListAndAttrsRecursive (v: v != null);
# Turns
@ -30,16 +42,22 @@ let
# {"if.foo" = "xxx"; "if.bar" = "yyy"}
# so that the correct TOML is generated for the
# on-window-detected table.
flattenConditions = attrs:
let conditions = attrs."if" or { };
in builtins.removeAttrs attrs [ "if" ]
// lib.concatMapAttrs (n: v: { "if.${n}" = v; }) conditions;
flattenConditions =
attrs:
let
conditions = attrs."if" or { };
in
builtins.removeAttrs attrs [ "if" ] // lib.concatMapAttrs (n: v: { "if.${n}" = v; }) conditions;
flattenOnWindowDetected = cfg:
let owd = cfg.on-window-detected or [ ];
in cfg // { on-window-detected = map flattenConditions owd; };
flattenOnWindowDetected =
cfg:
let
owd = cfg.on-window-detected or [ ];
in
cfg // { on-window-detected = map flattenConditions owd; };
in {
in
{
meta.maintainers = with lib.hm.maintainers; [ damidoug ];
options.programs.aerospace = {
@ -76,102 +94,122 @@ in {
enable-normalization-flatten-containers = mkOption {
type = types.bool;
default = true;
description =
''Containers that have only one child are "flattened".'';
description = ''Containers that have only one child are "flattened".'';
};
enable-normalization-opposite-orientation-for-nested-containers = mkOption {
type = types.bool;
default = true;
description = "Containers that nest into each other must have opposite orientations.";
};
enable-normalization-opposite-orientation-for-nested-containers =
mkOption {
type = types.bool;
default = true;
description =
"Containers that nest into each other must have opposite orientations.";
};
accordion-padding = mkOption {
type = types.int;
default = 30;
description = "Padding between windows in an accordion container.";
};
default-root-container-layout = mkOption {
type = types.enum [ "tiles" "accordion" ];
type = types.enum [
"tiles"
"accordion"
];
default = "tiles";
description = "Default layout for the root container.";
};
default-root-container-orientation = mkOption {
type = types.enum [ "horizontal" "vertical" "auto" ];
type = types.enum [
"horizontal"
"vertical"
"auto"
];
default = "auto";
description = "Default orientation for the root container.";
};
on-window-detected = mkOption {
type = types.listOf (types.submodule {
options = {
"if" = mkOption {
type = types.submodule {
options = {
app-id = mkOption {
type = with types; nullOr str;
default = null;
description = "The application ID to match (optional).";
};
workspace = mkOption {
type = with types; nullOr str;
default = null;
description = "The workspace name to match (optional).";
};
window-title-regex-substring = mkOption {
type = with types; nullOr str;
default = null;
description =
"Substring to match in the window title (optional).";
};
app-name-regex-substring = mkOption {
type = with types; nullOr str;
default = null;
description =
"Regex substring to match the app name (optional).";
};
during-aerospace-startup = mkOption {
type = with types; nullOr bool;
default = null;
description =
"Whether to match during aerospace startup (optional).";
type = types.listOf (
types.submodule {
options = {
"if" = mkOption {
type = types.submodule {
options = {
app-id = mkOption {
type = with types; nullOr str;
default = null;
description = "The application ID to match (optional).";
};
workspace = mkOption {
type = with types; nullOr str;
default = null;
description = "The workspace name to match (optional).";
};
window-title-regex-substring = mkOption {
type = with types; nullOr str;
default = null;
description = "Substring to match in the window title (optional).";
};
app-name-regex-substring = mkOption {
type = with types; nullOr str;
default = null;
description = "Regex substring to match the app name (optional).";
};
during-aerospace-startup = mkOption {
type = with types; nullOr bool;
default = null;
description = "Whether to match during aerospace startup (optional).";
};
};
};
default = { };
description = "Conditions for detecting a window.";
};
check-further-callbacks = mkOption {
type = with types; nullOr bool;
default = null;
description = "Whether to check further callbacks after this rule (optional).";
};
run = mkOption {
type =
with types;
oneOf [
str
(listOf str)
];
example = [
"move-node-to-workspace m"
"resize-node"
];
description = "Commands to execute when the conditions match (required).";
};
default = { };
description = "Conditions for detecting a window.";
};
check-further-callbacks = mkOption {
type = with types; nullOr bool;
default = null;
description =
"Whether to check further callbacks after this rule (optional).";
};
run = mkOption {
type = with types; oneOf [ str (listOf str) ];
example = [ "move-node-to-workspace m" "resize-node" ];
description =
"Commands to execute when the conditions match (required).";
};
};
});
}
);
default = [ ];
example = [{
"if" = {
app-id = "Another.Cool.App";
workspace = "cool-workspace";
window-title-regex-substring = "Title";
app-name-regex-substring = "CoolApp";
during-aerospace-startup = false;
};
check-further-callbacks = false;
run = [ "move-node-to-workspace m" "resize-node" ];
}];
description =
"Commands to run every time a new window is detected with optional conditions.";
example = [
{
"if" = {
app-id = "Another.Cool.App";
workspace = "cool-workspace";
window-title-regex-substring = "Title";
app-name-regex-substring = "CoolApp";
during-aerospace-startup = false;
};
check-further-callbacks = false;
run = [
"move-node-to-workspace m"
"resize-node"
];
}
];
description = "Commands to run every time a new window is detected with optional conditions.";
};
workspace-to-monitor-force-assignment = mkOption {
type = with types;
nullOr (attrsOf (oneOf [ int str (listOf str) ]));
type =
with types;
nullOr (
attrsOf (oneOf [
int
str
(listOf str)
])
);
default = null;
description = ''
Map workspaces to specific monitors.
@ -182,17 +220,18 @@ in {
"2" = "main"; # Main monitor.
"3" = "secondary"; # Secondary monitor (non-main).
"4" = "built-in"; # Built-in display.
"5" =
"^built-in retina display$"; # Regex for the built-in retina display.
"6" = [ "secondary" "dell" ]; # Match first pattern in the list.
"5" = "^built-in retina display$"; # Regex for the built-in retina display.
"6" = [
"secondary"
"dell"
]; # Match first pattern in the list.
};
};
on-focus-changed = mkOption {
type = with types; listOf str;
default = [ ];
example = [ "move-mouse monitor-lazy-center" ];
description =
"Commands to run every time focused window or workspace changes.";
description = "Commands to run every time focused window or workspace changes.";
};
on-focused-monitor-changed = mkOption {
type = with types; listOf str;
@ -210,7 +249,10 @@ in {
description = "Commands to run every time workspace changes.";
};
key-mapping.preset = mkOption {
type = types.enum [ "qwerty" "dvorak" ];
type = types.enum [
"qwerty"
"dvorak"
];
default = "qwerty";
description = "Keymapping preset.";
};
@ -243,15 +285,14 @@ in {
config = lib.mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "programs.aerospace" pkgs
lib.platforms.darwin)
(lib.hm.assertions.assertPlatform "programs.aerospace" pkgs lib.platforms.darwin)
];
home = {
packages = lib.mkIf (cfg.package != null) [ cfg.package ];
file.".config/aerospace/aerospace.toml".source =
tomlFormat.generate "aerospace"
(filterNulls (flattenOnWindowDetected cfg.userSettings));
file.".config/aerospace/aerospace.toml".source = tomlFormat.generate "aerospace" (
filterNulls (flattenOnWindowDetected cfg.userSettings)
);
};
};
}

View file

@ -1,6 +1,13 @@
{ config, lib, pkgs, ... }:
let cfg = config.programs.afew;
in {
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.afew;
in
{
options.programs.afew = {
enable = lib.mkEnableOption "the afew initial tagging script for Notmuch";

View file

@ -1,8 +1,14 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.alacritty;
tomlFormat = pkgs.formats.toml { };
in {
in
{
options = {
programs.alacritty = {
enable = lib.mkEnableOption "Alacritty";
@ -56,40 +62,45 @@ in {
};
config = lib.mkIf cfg.enable {
assertions = [{
# If using the theme option, ensure that theme exists in the
# alacritty-theme package.
assertion = let
available = lib.pipe "${pkgs.alacritty-theme}/share/alacritty-theme" [
builtins.readDir
(lib.filterAttrs
(name: type: type == "regular" && lib.hasSuffix ".toml" name))
lib.attrNames
(lib.map (lib.removeSuffix ".toml"))
];
in cfg.theme == null || (builtins.elem cfg.theme available);
message = "The alacritty theme '${cfg.theme}' does not exist.";
}];
assertions = [
{
# If using the theme option, ensure that theme exists in the
# alacritty-theme package.
assertion =
let
available = lib.pipe "${pkgs.alacritty-theme}/share/alacritty-theme" [
builtins.readDir
(lib.filterAttrs (name: type: type == "regular" && lib.hasSuffix ".toml" name))
lib.attrNames
(lib.map (lib.removeSuffix ".toml"))
];
in
cfg.theme == null || (builtins.elem cfg.theme available);
message = "The alacritty theme '${cfg.theme}' does not exist.";
}
];
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
programs.alacritty.settings = let
theme = "${pkgs.alacritty-theme}/share/alacritty-theme/${cfg.theme}.toml";
in lib.mkIf (cfg.theme != null) {
general.import =
lib.mkIf (lib.versionAtLeast cfg.package.version "0.14") [ theme ];
import = lib.mkIf (lib.versionOlder cfg.package.version "0.14") [ theme ];
};
programs.alacritty.settings =
let
theme = "${pkgs.alacritty-theme}/share/alacritty-theme/${cfg.theme}.toml";
in
lib.mkIf (cfg.theme != null) {
general.import = lib.mkIf (lib.versionAtLeast cfg.package.version "0.14") [ theme ];
import = lib.mkIf (lib.versionOlder cfg.package.version "0.14") [ theme ];
};
xdg.configFile."alacritty/alacritty.toml" = lib.mkIf (cfg.settings != { }) {
source = (tomlFormat.generate "alacritty.toml" cfg.settings).overrideAttrs
(finalAttrs: prevAttrs: {
source = (tomlFormat.generate "alacritty.toml" cfg.settings).overrideAttrs (
finalAttrs: prevAttrs: {
buildCommand = lib.concatStringsSep "\n" [
prevAttrs.buildCommand
# TODO: why is this needed? Is there a better way to retain escape sequences?
"substituteInPlace $out --replace-quiet '\\\\' '\\'"
];
});
}
);
};
};
}

View file

@ -1,7 +1,9 @@
pkgs:
{ config, lib, ... }:
let inherit (lib) mkOption types;
in {
let
inherit (lib) mkOption types;
in
{
options.alot = {
sendMailCommand = mkOption {
type = types.nullOr types.str;
@ -16,11 +18,9 @@ in {
type = types.attrsOf types.str;
default = {
type = "shellcommand";
command =
"'${pkgs.notmuch}/bin/notmuch address --format=json --output=recipients date:6M..'";
regexp = "'\\[?{" + ''
"name": "(?P<name>.*)", "address": "(?P<email>.+)", "name-addr": ".*"''
+ "}[,\\]]?'";
command = "'${pkgs.notmuch}/bin/notmuch address --format=json --output=recipients date:6M..'";
regexp =
"'\\[?{" + ''"name": "(?P<name>.*)", "address": "(?P<email>.+)", "name-addr": ".*"'' + "}[,\\]]?'";
shellcommand_external_filtering = "False";
};
example = lib.literalExpression ''
@ -48,9 +48,8 @@ in {
};
config = lib.mkIf config.notmuch.enable {
alot.sendMailCommand = lib.mkOptionDefault (if config.msmtp.enable then
"msmtpq --read-envelope-from --read-recipients"
else
null);
alot.sendMailCommand = lib.mkOptionDefault (
if config.msmtp.enable then "msmtpq --read-envelope-from --read-recipients" else null
);
};
}

View file

@ -1,22 +1,35 @@
# alot config loader is sensitive to leading space !
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
concatStringsSep mapAttrsToList mkOption optionalAttrs optionalString types;
concatStringsSep
mapAttrsToList
mkOption
optionalAttrs
optionalString
types
;
cfg = config.programs.alot;
enabledAccounts = lib.filter (a: a.notmuch.enable)
(lib.attrValues config.accounts.email.accounts);
enabledAccounts = lib.filter (a: a.notmuch.enable) (lib.attrValues config.accounts.email.accounts);
# sorted: primary first
alotAccounts = lib.sort (a: b: !(a.primary -> b.primary)) enabledAccounts;
boolStr = v: if v then "True" else "False";
mkKeyValue = key: value:
let value' = if lib.isBool value then boolStr value else toString value;
in "${key} = ${value'}";
mkKeyValue =
key: value:
let
value' = if lib.isBool value then boolStr value else toString value;
in
"${key} = ${value'}";
mk2ndLevelSectionName = name: "[" + name + "]";
@ -59,67 +72,95 @@ let
};
};
accountStr = account:
accountStr =
account:
let
inherit (account)
alot maildir name address realName folders aliases gpg signature;
in concatStringsSep "\n" ([ "[[${name}]]" ]
++ mapAttrsToList (n: v: n + "=" + v) ({
address = address;
realname = realName;
sendmail_command =
optionalString (alot.sendMailCommand != null) alot.sendMailCommand;
} // optionalAttrs (folders.sent != null) {
sent_box = "maildir" + "://" + maildir.absPath + "/" + folders.sent;
} // optionalAttrs (folders.drafts != null) {
draft_box = "maildir" + "://" + maildir.absPath + "/" + folders.drafts;
} // optionalAttrs (aliases != [ ]) {
aliases = concatStringsSep "," aliases;
} // optionalAttrs (gpg != null) {
gpg_key = gpg.key;
encrypt_by_default = if gpg.encryptByDefault then "all" else "none";
sign_by_default = boolStr gpg.signByDefault;
} // optionalAttrs (signature.showSignature != "none") {
signature = pkgs.writeText "signature.txt" signature.text;
signature_as_attachment = boolStr (signature.showSignature == "attach");
}) ++ [ alot.extraConfig ] ++ [ "[[[abook]]]" ]
++ mapAttrsToList (n: v: n + "=" + v) alot.contactCompletion);
alot
maildir
name
address
realName
folders
aliases
gpg
signature
;
in
concatStringsSep "\n" (
[ "[[${name}]]" ]
++ mapAttrsToList (n: v: n + "=" + v) (
{
address = address;
realname = realName;
sendmail_command = optionalString (alot.sendMailCommand != null) alot.sendMailCommand;
}
// optionalAttrs (folders.sent != null) {
sent_box = "maildir" + "://" + maildir.absPath + "/" + folders.sent;
}
// optionalAttrs (folders.drafts != null) {
draft_box = "maildir" + "://" + maildir.absPath + "/" + folders.drafts;
}
// optionalAttrs (aliases != [ ]) {
aliases = concatStringsSep "," aliases;
}
// optionalAttrs (gpg != null) {
gpg_key = gpg.key;
encrypt_by_default = if gpg.encryptByDefault then "all" else "none";
sign_by_default = boolStr gpg.signByDefault;
}
// optionalAttrs (signature.showSignature != "none") {
signature = pkgs.writeText "signature.txt" signature.text;
signature_as_attachment = boolStr (signature.showSignature == "attach");
}
)
++ [ alot.extraConfig ]
++ [ "[[[abook]]]" ]
++ mapAttrsToList (n: v: n + "=" + v) alot.contactCompletion
);
configFile = let
bindingsToStr = attrSet:
concatStringsSep "\n" (mapAttrsToList (n: v: "${n} = ${v}") attrSet);
in ''
# Generated by Home Manager.
# See http://alot.readthedocs.io/en/latest/configuration/config_options.html
configFile =
let
bindingsToStr = attrSet: concatStringsSep "\n" (mapAttrsToList (n: v: "${n} = ${v}") attrSet);
in
''
# Generated by Home Manager.
# See http://alot.readthedocs.io/en/latest/configuration/config_options.html
${lib.generators.toKeyValue { inherit mkKeyValue; } cfg.settings}
${cfg.extraConfig}
[tags]
'' + (let
submoduleToAttrs = m:
lib.filterAttrs (name: v: name != "_module" && v != null) m;
in lib.generators.toINI { mkSectionName = mk2ndLevelSectionName; }
(lib.mapAttrs (name: x: submoduleToAttrs x) cfg.tags)) + ''
[bindings]
${bindingsToStr cfg.bindings.global}
${lib.generators.toKeyValue { inherit mkKeyValue; } cfg.settings}
${cfg.extraConfig}
[tags]
''
+ (
let
submoduleToAttrs = m: lib.filterAttrs (name: v: name != "_module" && v != null) m;
in
lib.generators.toINI { mkSectionName = mk2ndLevelSectionName; } (
lib.mapAttrs (name: x: submoduleToAttrs x) cfg.tags
)
)
+ ''
[bindings]
${bindingsToStr cfg.bindings.global}
[[bufferlist]]
${bindingsToStr cfg.bindings.bufferlist}
[[search]]
${bindingsToStr cfg.bindings.search}
[[envelope]]
${bindingsToStr cfg.bindings.envelope}
[[taglist]]
${bindingsToStr cfg.bindings.taglist}
[[thread]]
${bindingsToStr cfg.bindings.thread}
[[bufferlist]]
${bindingsToStr cfg.bindings.bufferlist}
[[search]]
${bindingsToStr cfg.bindings.search}
[[envelope]]
${bindingsToStr cfg.bindings.envelope}
[[taglist]]
${bindingsToStr cfg.bindings.taglist}
[[thread]]
${bindingsToStr cfg.bindings.thread}
[accounts]
[accounts]
${lib.concatStringsSep "\n\n" (map accountStr alotAccounts)}
'';
${lib.concatStringsSep "\n\n" (map accountStr alotAccounts)}
'';
in {
in
{
options = {
programs.alot = {
enable = mkOption {
@ -196,9 +237,12 @@ in {
};
settings = mkOption {
type = with types;
let primitive = either (either (either str int) bool) float;
in attrsOf primitive;
type =
with types;
let
primitive = either (either (either str int) bool) float;
in
attrsOf primitive;
default = {
initial_command = "search tag:inbox AND NOT tag:killed";
auto_remove_unread = true;
@ -237,9 +281,11 @@ in {
xdg.configFile."alot/config".text = configFile;
xdg.configFile."alot/hooks.py" = lib.mkIf (cfg.hooks != "") {
text = ''
# Generated by Home Manager.
'' + cfg.hooks;
text =
''
# Generated by Home Manager.
''
+ cfg.hooks;
};
};
}

View file

@ -1,15 +1,25 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.zsh.antidote;
zPluginStr = (pluginNames:
lib.optionalString (pluginNames != [ ]) "${lib.concatStrings (map (name: ''
${name}
'') pluginNames)}");
zPluginStr = (
pluginNames:
lib.optionalString (pluginNames != [ ])
"${lib.concatStrings (
map (name: ''
${name}
'') pluginNames
)}"
);
parseHashId = path:
lib.elemAt (builtins.match "${builtins.storeDir}/([a-zA-Z0-9]+)-.*" path) 0;
in {
parseHashId = path: lib.elemAt (builtins.match "${builtins.storeDir}/([a-zA-Z0-9]+)-.*" path) 0;
in
{
meta.maintainers = [ lib.maintainers.hitsmaxft ];
options.programs.zsh.antidote = {
@ -30,25 +40,26 @@ in {
config = lib.mkIf cfg.enable {
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
programs.zsh.initContent = let
configFiles = pkgs.runCommand "hm_antidote-files" { } ''
echo "${zPluginStr cfg.plugins}" > $out
'';
hashId = parseHashId "${configFiles}";
in (lib.mkOrder 550 ''
## home-manager/antidote begin :
source ${cfg.package}/share/antidote/antidote.zsh
${lib.optionalString cfg.useFriendlyNames
"zstyle ':antidote:bundle' use-friendly-names 'yes'"}
programs.zsh.initContent =
let
configFiles = pkgs.runCommand "hm_antidote-files" { } ''
echo "${zPluginStr cfg.plugins}" > $out
'';
hashId = parseHashId "${configFiles}";
in
(lib.mkOrder 550 ''
## home-manager/antidote begin :
source ${cfg.package}/share/antidote/antidote.zsh
${lib.optionalString cfg.useFriendlyNames "zstyle ':antidote:bundle' use-friendly-names 'yes'"}
bundlefile=${configFiles}
zstyle ':antidote:bundle' file $bundlefile
staticfile=/tmp/tmp_hm_zsh_plugins.zsh-${hashId}
zstyle ':antidote:static' file $staticfile
bundlefile=${configFiles}
zstyle ':antidote:bundle' file $bundlefile
staticfile=/tmp/tmp_hm_zsh_plugins.zsh-${hashId}
zstyle ':antidote:static' file $staticfile
antidote load $bundlefile $staticfile
antidote load $bundlefile $staticfile
## home-manager/antidote end
'');
## home-manager/antidote end
'');
};
}

View file

@ -1,16 +1,20 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.aria2;
formatLine = n: v:
formatLine =
n: v:
let
formatValue = v:
if builtins.isBool v then
(if v then "true" else "false")
else
toString v;
in "${n}=${formatValue v}";
in {
formatValue = v: if builtins.isBool v then (if v then "true" else "false") else toString v;
in
"${n}=${formatValue v}";
in
{
meta.maintainers = [ lib.hm.maintainers.justinlovinger ];
options.programs.aria2 = {
@ -19,7 +23,14 @@ in {
package = lib.mkPackageOption pkgs "aria2" { nullable = true; };
settings = lib.mkOption {
type = with lib.types; attrsOf (oneOf [ bool float int str ]);
type =
with lib.types;
attrsOf (oneOf [
bool
float
int
str
]);
default = { };
description = ''
Options to add to {file}`aria2.conf` file.
@ -50,8 +61,10 @@ in {
config = lib.mkIf cfg.enable {
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
xdg.configFile."aria2/aria2.conf".text = lib.concatStringsSep "\n" ([ ]
xdg.configFile."aria2/aria2.conf".text = lib.concatStringsSep "\n" (
[ ]
++ lib.mapAttrsToList formatLine cfg.settings
++ lib.optional (cfg.extraConfig != "") cfg.extraConfig);
++ lib.optional (cfg.extraConfig != "") cfg.extraConfig
);
};
}

View file

@ -1,4 +1,5 @@
{ config, lib, ... }: {
{ config, lib, ... }:
{
options.astroid = {
enable = lib.mkEnableOption "Astroid";
@ -14,7 +15,9 @@
extraConfig = lib.mkOption {
type = lib.types.attrsOf lib.types.anything;
default = { };
example = { select_query = ""; };
example = {
select_query = "";
};
description = ''
Extra settings to add to this astroid account configuration.
'';
@ -22,7 +25,8 @@
};
config = lib.mkIf config.notmuch.enable {
astroid.sendMailCommand = lib.mkIf config.msmtp.enable
(lib.mkOptionDefault "msmtpq --read-envelope-from --read-recipients");
astroid.sendMailCommand = lib.mkIf config.msmtp.enable (
lib.mkOptionDefault "msmtpq --read-envelope-from --read-recipients"
);
};
}

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkOption types;
@ -6,12 +11,12 @@ let
jsonFormat = pkgs.formats.json { };
astroidAccounts =
lib.filterAttrs (n: v: v.astroid.enable) config.accounts.email.accounts;
astroidAccounts = lib.filterAttrs (n: v: v.astroid.enable) config.accounts.email.accounts;
boolOpt = b: if b then "true" else "false";
accountAttr = account:
accountAttr =
account:
with account;
{
email = address;
@ -23,33 +28,38 @@ let
save_sent = "true";
save_sent_to = "${maildir.absPath}/${folders.sent}/cur/";
select_query = "";
} // lib.optionalAttrs (signature.showSignature != "none") {
}
// lib.optionalAttrs (signature.showSignature != "none") {
signature_attach = boolOpt (signature.showSignature == "attach");
signature_default_on = boolOpt (signature.showSignature != "none");
signature_file = pkgs.writeText "signature.txt" signature.text;
signature_file_markdown = "false";
signature_separate = "true"; # prepends '--\n' to the signature
} // lib.optionalAttrs (gpg != null) {
}
// lib.optionalAttrs (gpg != null) {
always_gpg_sign = boolOpt gpg.signByDefault;
gpgkey = gpg.key;
} // astroid.extraConfig;
}
// astroid.extraConfig;
# See https://github.com/astroidmail/astroid/wiki/Configuration-Reference
finalConfig = let
template = lib.importJSON ./astroid-config-template.json;
astroidConfig = lib.foldl' lib.recursiveUpdate template [
{
astroid.notmuch_config =
"${config.xdg.configHome}/notmuch/default/config";
accounts = lib.mapAttrs (n: accountAttr) astroidAccounts;
crypto.gpg.path = "${pkgs.gnupg}/bin/gpg";
}
cfg.extraConfig
cfg.externalEditor
];
in astroidConfig;
finalConfig =
let
template = lib.importJSON ./astroid-config-template.json;
astroidConfig = lib.foldl' lib.recursiveUpdate template [
{
astroid.notmuch_config = "${config.xdg.configHome}/notmuch/default/config";
accounts = lib.mapAttrs (n: accountAttr) astroidAccounts;
crypto.gpg.path = "${pkgs.gnupg}/bin/gpg";
}
cfg.extraConfig
cfg.externalEditor
];
in
astroidConfig;
in {
in
{
options = {
programs.astroid = {
enable = lib.mkEnableOption "Astroid";
@ -69,15 +79,15 @@ in {
type = types.nullOr types.str;
default = null;
# Converts it into JSON that can be merged into the configuration.
apply = cmd:
apply =
cmd:
lib.optionalAttrs (cmd != null) {
editor = {
"external_editor" = "true";
"cmd" = cmd;
};
};
example =
"nvim-qt -- -c 'set ft=mail' '+set fileencoding=utf-8' '+set ff=unix' '+set enc=utf-8' '+set fo+=w' %1";
example = "nvim-qt -- -c 'set ft=mail' '+set fileencoding=utf-8' '+set ff=unix' '+set enc=utf-8' '+set fo+=w' %1";
description = ''
You can use the following variables:
@ -117,8 +127,7 @@ in {
config = lib.mkIf cfg.enable {
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
xdg.configFile."astroid/config".source =
jsonFormat.generate "astroid-config" finalConfig;
xdg.configFile."astroid/config".source = jsonFormat.generate "astroid-config" finalConfig;
xdg.configFile."astroid/poll.sh" = lib.mkIf (cfg.pollScript != "") {
executable = true;

View file

@ -1,4 +1,9 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
let
cfg = config.programs.atuin;
daemonCfg = cfg.daemon;
@ -7,8 +12,12 @@ let
inherit (lib) mkIf mkOption types;
inherit (pkgs.stdenv) isLinux isDarwin;
in {
meta.maintainers = with lib.maintainers; [ hawkw water-sucks ];
in
{
meta.maintainers = with lib.maintainers; [
hawkw
water-sucks
];
options.programs.atuin = {
enable = lib.mkEnableOption "atuin";
@ -17,18 +26,15 @@ in {
enableBashIntegration = lib.hm.shell.mkBashIntegrationOption {
inherit config;
extraDescription =
"If enabled, this will bind `ctrl-r` to open the Atuin history.";
extraDescription = "If enabled, this will bind `ctrl-r` to open the Atuin history.";
};
enableFishIntegration = lib.hm.shell.mkFishIntegrationOption {
inherit config;
extraDescription =
"If enabled, this will bind the up-arrow key to open the Atuin history.";
extraDescription = "If enabled, this will bind the up-arrow key to open the Atuin history.";
};
enableNushellIntegration =
lib.hm.shell.mkNushellIntegrationOption { inherit config; };
enableNushellIntegration = lib.hm.shell.mkNushellIntegrationOption { inherit config; };
enableZshIntegration = lib.hm.shell.mkZshIntegrationOption {
inherit config;
@ -41,21 +47,30 @@ in {
flags = mkOption {
default = [ ];
type = types.listOf types.str;
example = [ "--disable-up-arrow" "--disable-ctrl-r" ];
example = [
"--disable-up-arrow"
"--disable-ctrl-r"
];
description = ''
Flags to append to the shell hook.
'';
};
settings = mkOption {
type = with types;
type =
with types;
let
prim = oneOf [ bool int str ];
prim = oneOf [
bool
int
str
];
primOrPrimAttrs = either prim (attrsOf prim);
entry = either prim (listOf primOrPrimAttrs);
entryOrAttrsOf = t: either entry (attrsOf t);
entries = entryOrAttrsOf (entryOrAttrsOf entry);
in attrsOf entries // { description = "Atuin configuration"; };
in
attrsOf entries // { description = "Atuin configuration"; };
default = { };
example = lib.literalExpression ''
{
@ -79,8 +94,15 @@ in {
logLevel = mkOption {
default = null;
type =
types.nullOr (types.enum [ "trace" "debug" "info" "warn" "error" ]);
type = types.nullOr (
types.enum [
"trace"
"debug"
"info"
"warn"
"error"
]
);
description = ''
Verbosity of Atuin daemon logging.
'';
@ -88,126 +110,145 @@ in {
};
};
config = let flagsStr = lib.escapeShellArgs cfg.flags;
in mkIf cfg.enable (lib.mkMerge [
{
# Always add the configured `atuin` package.
home.packages = [ cfg.package ];
config =
let
flagsStr = lib.escapeShellArgs cfg.flags;
in
mkIf cfg.enable (
lib.mkMerge [
{
# Always add the configured `atuin` package.
home.packages = [ cfg.package ];
# If there are user-provided settings, generate the config file.
xdg.configFile."atuin/config.toml" = mkIf (cfg.settings != { }) {
source = tomlFormat.generate "atuin-config" cfg.settings;
};
# If there are user-provided settings, generate the config file.
xdg.configFile."atuin/config.toml" = mkIf (cfg.settings != { }) {
source = tomlFormat.generate "atuin-config" cfg.settings;
};
programs.bash.initExtra = mkIf cfg.enableBashIntegration ''
if [[ :$SHELLOPTS: =~ :(vi|emacs): ]]; then
source "${pkgs.bash-preexec}/share/bash/bash-preexec.sh"
eval "$(${lib.getExe cfg.package} init bash ${flagsStr})"
fi
'';
programs.bash.initExtra = mkIf cfg.enableBashIntegration ''
if [[ :$SHELLOPTS: =~ :(vi|emacs): ]]; then
source "${pkgs.bash-preexec}/share/bash/bash-preexec.sh"
eval "$(${lib.getExe cfg.package} init bash ${flagsStr})"
fi
'';
programs.zsh.initContent = mkIf cfg.enableZshIntegration ''
if [[ $options[zle] = on ]]; then
eval "$(${lib.getExe cfg.package} init zsh ${flagsStr})"
fi
'';
programs.zsh.initContent = mkIf cfg.enableZshIntegration ''
if [[ $options[zle] = on ]]; then
eval "$(${lib.getExe cfg.package} init zsh ${flagsStr})"
fi
'';
programs.fish.interactiveShellInit = mkIf cfg.enableFishIntegration ''
${lib.getExe cfg.package} init fish ${flagsStr} | source
'';
programs.fish.interactiveShellInit = mkIf cfg.enableFishIntegration ''
${lib.getExe cfg.package} init fish ${flagsStr} | source
'';
programs.nushell = mkIf cfg.enableNushellIntegration {
extraConfig = ''
source ${
pkgs.runCommand "atuin-nushell-config.nu" {
nativeBuildInputs = [ pkgs.writableTmpDirAsHomeHook ];
} ''
${lib.getExe cfg.package} init nu ${flagsStr} >> "$out"
''
}
'';
};
}
(mkIf daemonCfg.enable (lib.mkMerge [
{
assertions = [
{
assertion = lib.versionAtLeast cfg.package.version "18.2.0";
message = ''
The Atuin daemon requires at least version 18.2.0 or later.
programs.nushell = mkIf cfg.enableNushellIntegration {
extraConfig = ''
source ${
pkgs.runCommand "atuin-nushell-config.nu"
{
nativeBuildInputs = [ pkgs.writableTmpDirAsHomeHook ];
}
''
${lib.getExe cfg.package} init nu ${flagsStr} >> "$out"
''
}
'';
}
{
assertion = isLinux || isDarwin;
message =
"The Atuin daemon can only be configured on either Linux or macOS.";
}
];
};
}
programs.atuin.settings = { daemon = { enabled = true; }; };
}
(mkIf isLinux {
programs.atuin.settings = { daemon = { systemd_socket = true; }; };
(mkIf daemonCfg.enable (
lib.mkMerge [
{
assertions = [
{
assertion = lib.versionAtLeast cfg.package.version "18.2.0";
message = ''
The Atuin daemon requires at least version 18.2.0 or later.
'';
}
{
assertion = isLinux || isDarwin;
message = "The Atuin daemon can only be configured on either Linux or macOS.";
}
];
systemd.user.services.atuin-daemon = {
Unit = {
Description = "Atuin daemon";
Requires = [ "atuin-daemon.socket" ];
};
Install = {
Also = [ "atuin-daemon.socket" ];
WantedBy = [ "default.target" ];
};
Service = {
ExecStart = "${lib.getExe cfg.package} daemon";
Environment = lib.optionals (daemonCfg.logLevel != null)
[ "ATUIN_LOG=${daemonCfg.logLevel}" ];
Restart = "on-failure";
RestartSteps = 3;
RestartMaxDelaySec = 6;
};
};
systemd.user.sockets.atuin-daemon = let
socket_dir = if lib.versionAtLeast cfg.package.version "18.4.0" then
"%t"
else
"%D/atuin";
in {
Unit = { Description = "Atuin daemon socket"; };
Install = { WantedBy = [ "sockets.target" ]; };
Socket = {
ListenStream = "${socket_dir}/atuin.sock";
SocketMode = "0600";
RemoveOnStop = true;
};
};
})
(mkIf isDarwin {
programs.atuin.settings = {
daemon = {
socket_path =
lib.mkDefault "${config.xdg.dataHome}/atuin/daemon.sock";
};
};
launchd.agents.atuin-daemon = {
enable = true;
config = {
ProgramArguments = [ "${lib.getExe cfg.package}" "daemon" ];
EnvironmentVariables =
lib.optionalAttrs (daemonCfg.logLevel != null) {
ATUIN_LOG = daemonCfg.logLevel;
programs.atuin.settings = {
daemon = {
enabled = true;
};
};
KeepAlive = {
Crashed = true;
SuccessfulExit = false;
};
ProcessType = "Background";
};
};
})
]))
]);
}
(mkIf isLinux {
programs.atuin.settings = {
daemon = {
systemd_socket = true;
};
};
systemd.user.services.atuin-daemon = {
Unit = {
Description = "Atuin daemon";
Requires = [ "atuin-daemon.socket" ];
};
Install = {
Also = [ "atuin-daemon.socket" ];
WantedBy = [ "default.target" ];
};
Service = {
ExecStart = "${lib.getExe cfg.package} daemon";
Environment = lib.optionals (daemonCfg.logLevel != null) [ "ATUIN_LOG=${daemonCfg.logLevel}" ];
Restart = "on-failure";
RestartSteps = 3;
RestartMaxDelaySec = 6;
};
};
systemd.user.sockets.atuin-daemon =
let
socket_dir = if lib.versionAtLeast cfg.package.version "18.4.0" then "%t" else "%D/atuin";
in
{
Unit = {
Description = "Atuin daemon socket";
};
Install = {
WantedBy = [ "sockets.target" ];
};
Socket = {
ListenStream = "${socket_dir}/atuin.sock";
SocketMode = "0600";
RemoveOnStop = true;
};
};
})
(mkIf isDarwin {
programs.atuin.settings = {
daemon = {
socket_path = lib.mkDefault "${config.xdg.dataHome}/atuin/daemon.sock";
};
};
launchd.agents.atuin-daemon = {
enable = true;
config = {
ProgramArguments = [
"${lib.getExe cfg.package}"
"daemon"
];
EnvironmentVariables = lib.optionalAttrs (daemonCfg.logLevel != null) {
ATUIN_LOG = daemonCfg.logLevel;
};
KeepAlive = {
Crashed = true;
SuccessfulExit = false;
};
ProcessType = "Background";
};
};
})
]
))
]
);
}

View file

@ -1,9 +1,15 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.autojump;
inherit (lib) mkIf;
in {
in
{
meta.maintainers = [ lib.maintainers.evanjs ];
options.programs.autojump = {
@ -11,22 +17,21 @@ in {
package = lib.mkPackageOption pkgs "autojump" { };
enableBashIntegration =
lib.hm.shell.mkBashIntegrationOption { inherit config; };
enableBashIntegration = lib.hm.shell.mkBashIntegrationOption { inherit config; };
enableFishIntegration =
lib.hm.shell.mkFishIntegrationOption { inherit config; };
enableFishIntegration = lib.hm.shell.mkFishIntegrationOption { inherit config; };
enableZshIntegration =
lib.hm.shell.mkZshIntegrationOption { inherit config; };
enableZshIntegration = lib.hm.shell.mkZshIntegrationOption { inherit config; };
};
config = mkIf cfg.enable {
home.packages = [ cfg.package ];
programs.bash.initExtra = mkIf cfg.enableBashIntegration (lib.mkBefore ''
. ${cfg.package}/share/autojump/autojump.bash
'');
programs.bash.initExtra = mkIf cfg.enableBashIntegration (
lib.mkBefore ''
. ${cfg.package}/share/autojump/autojump.bash
''
);
programs.zsh.initContent = mkIf cfg.enableZshIntegration ''
. ${cfg.package}/share/autojump/autojump.zsh

View file

@ -1,25 +1,49 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
literalExpression listToAttrs mapAttrs' mapAttrsToList mkIf mkOption
optional types;
literalExpression
listToAttrs
mapAttrs'
mapAttrsToList
mkIf
mkOption
optional
types
;
cfg = config.programs.autorandr;
matrixOf = n: m: elemType:
matrixOf =
n: m: elemType:
lib.mkOptionType rec {
name = "matrixOf";
description =
"${toString n}×${toString m} matrix of ${elemType.description}s";
check = xss:
let listOfSize = l: xs: lib.isList xs && lib.length xs == l;
in listOfSize n xss
&& lib.all (xs: listOfSize m xs && lib.all elemType.check xs) xss;
description = "${toString n}×${toString m} matrix of ${elemType.description}s";
check =
xss:
let
listOfSize = l: xs: lib.isList xs && lib.length xs == l;
in
listOfSize n xss && lib.all (xs: listOfSize m xs && lib.all elemType.check xs) xss;
merge = lib.mergeOneOption;
getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "*" "*" ]);
getSubOptions =
prefix:
elemType.getSubOptions (
prefix
++ [
"*"
"*"
]
);
getSubModules = elemType.getSubModules;
substSubModules = mod: matrixOf n m (elemType.substSubModules mod);
functor = (lib.defaultFunctor name) // { wrapped = elemType; };
functor = (lib.defaultFunctor name) // {
wrapped = elemType;
};
};
profileModule = types.submodule {
@ -97,7 +121,14 @@ let
};
rotate = mkOption {
type = types.nullOr (types.enum [ "normal" "left" "right" "inverted" ]);
type = types.nullOr (
types.enum [
"normal"
"left"
"right"
"inverted"
]
);
description = "Output rotate configuration.";
default = null;
example = "left";
@ -128,26 +159,31 @@ let
};
scale = mkOption {
type = types.nullOr (types.submodule {
options = {
method = mkOption {
type = types.enum [ "factor" "pixel" ];
description = "Output scaling method.";
default = "factor";
example = "pixel";
};
type = types.nullOr (
types.submodule {
options = {
method = mkOption {
type = types.enum [
"factor"
"pixel"
];
description = "Output scaling method.";
default = "factor";
example = "pixel";
};
x = mkOption {
type = types.either types.float types.ints.positive;
description = "Horizontal scaling factor/pixels.";
};
x = mkOption {
type = types.either types.float types.ints.positive;
description = "Horizontal scaling factor/pixels.";
};
y = mkOption {
type = types.either types.float types.ints.positive;
description = "Vertical scaling factor/pixels.";
y = mkOption {
type = types.either types.float types.ints.positive;
description = "Vertical scaling factor/pixels.";
};
};
};
});
}
);
description = ''
Output scale configuration.
@ -172,7 +208,12 @@ let
};
filter = mkOption {
type = types.nullOr (types.enum [ "bilinear" "nearest" ]);
type = types.nullOr (
types.enum [
"bilinear"
"nearest"
]
);
description = "Interpolation method to be used for scaling the output.";
default = null;
example = "nearest";
@ -242,30 +283,35 @@ let
};
};
hookToFile = folder: name: hook:
hookToFile =
folder: name: hook:
lib.nameValuePair "autorandr/${folder}/${name}" {
source = "${pkgs.writeShellScriptBin "hook" hook}/bin/hook";
};
profileToFiles = name: profile:
let inherit (profile) hooks;
in lib.mkMerge [
profileToFiles =
name: profile:
let
inherit (profile) hooks;
in
lib.mkMerge [
{
"autorandr/${name}/setup".text = lib.concatStringsSep "\n"
(mapAttrsToList fingerprintToString profile.fingerprint);
"autorandr/${name}/config".text = lib.concatStringsSep "\n"
(mapAttrsToList configToString profile.config);
"autorandr/${name}/setup".text = lib.concatStringsSep "\n" (
mapAttrsToList fingerprintToString profile.fingerprint
);
"autorandr/${name}/config".text = lib.concatStringsSep "\n" (
mapAttrsToList configToString profile.config
);
}
(mkIf (hooks.postswitch != "")
(listToAttrs [ (hookToFile name "postswitch" hooks.postswitch) ]))
(mkIf (hooks.preswitch != "")
(listToAttrs [ (hookToFile name "preswitch" hooks.preswitch) ]))
(mkIf (hooks.predetect != "")
(listToAttrs [ (hookToFile name "predetect" hooks.predetect) ]))
(mkIf (hooks.postswitch != "") (listToAttrs [ (hookToFile name "postswitch" hooks.postswitch) ]))
(mkIf (hooks.preswitch != "") (listToAttrs [ (hookToFile name "preswitch" hooks.preswitch) ]))
(mkIf (hooks.predetect != "") (listToAttrs [ (hookToFile name "predetect" hooks.predetect) ]))
];
fingerprintToString = name: edid: "${name} ${edid}";
configToString = name: config:
configToString =
name: config:
if config.enable then
lib.concatStringsSep "\n" ([ "output ${name}" ]
lib.concatStringsSep "\n" (
[ "output ${name}" ]
++ optional (config.position != "") "pos ${config.position}"
++ optional (config.crtc != null) "crtc ${toString config.crtc}"
++ optional config.primary "primary"
@ -275,18 +321,23 @@ let
++ optional (config.rate != "") "rate ${config.rate}"
++ optional (config.rotate != null) "rotate ${config.rotate}"
++ optional (config.filter != null) "filter ${config.filter}"
++ optional (config.transform != null) ("transform "
+ lib.concatMapStringsSep "," toString (lib.flatten config.transform))
++ optional (config.scale != null)
((if config.scale.method == "factor" then "scale" else "scale-from")
+ " ${toString config.scale.x}x${toString config.scale.y}")
++ optional (config.extraConfig != "") config.extraConfig)
else ''
output ${name}
off
'';
++ optional (config.transform != null) (
"transform " + lib.concatMapStringsSep "," toString (lib.flatten config.transform)
)
++ optional (config.scale != null) (
(if config.scale.method == "factor" then "scale" else "scale-from")
+ " ${toString config.scale.x}x${toString config.scale.y}"
)
++ optional (config.extraConfig != "") config.extraConfig
)
else
''
output ${name}
off
'';
in {
in
{
options = {
programs.autorandr = {
enable = lib.mkEnableOption "Autorandr";
@ -358,15 +409,19 @@ in {
};
config = lib.mkIf cfg.enable {
assertions = lib.flatten (mapAttrsToList (profile:
{ config, ... }:
mapAttrsToList (output: opts: {
assertion = opts.scale == null || opts.transform == null;
message = ''
Cannot use the profile output options 'scale' and 'transform' simultaneously.
Check configuration for: programs.autorandr.profiles.${profile}.config.${output}
'';
}) config) cfg.profiles);
assertions = lib.flatten (
mapAttrsToList (
profile:
{ config, ... }:
mapAttrsToList (output: opts: {
assertion = opts.scale == null || opts.transform == null;
message = ''
Cannot use the profile output options 'scale' and 'transform' simultaneously.
Check configuration for: programs.autorandr.profiles.${profile}.config.${output}
'';
}) config
) cfg.profiles
);
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];

View file

@ -1,10 +1,16 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.awscli;
iniFormat = pkgs.formats.ini { };
in {
in
{
meta.maintainers = [ lib.maintainers.anthonyroussel ];
options.programs.awscli = {
@ -55,16 +61,12 @@ in {
config = lib.mkIf cfg.enable {
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
home.file."${config.home.homeDirectory}/.aws/config" =
lib.mkIf (cfg.settings != { }) {
source =
iniFormat.generate "aws-config-${config.home.username}" cfg.settings;
};
home.file."${config.home.homeDirectory}/.aws/config" = lib.mkIf (cfg.settings != { }) {
source = iniFormat.generate "aws-config-${config.home.username}" cfg.settings;
};
home.file."${config.home.homeDirectory}/.aws/credentials" =
lib.mkIf (cfg.credentials != { }) {
source = iniFormat.generate "aws-credentials-${config.home.username}"
cfg.credentials;
};
home.file."${config.home.homeDirectory}/.aws/credentials" = lib.mkIf (cfg.credentials != { }) {
source = iniFormat.generate "aws-credentials-${config.home.username}" cfg.credentials;
};
};
}

View file

@ -1,16 +1,23 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.bacon;
settingsFormat = pkgs.formats.toml { };
configDir = if pkgs.stdenv.isDarwin then
"Library/Application Support/org.dystroy.bacon"
else
"${config.xdg.configHome}/bacon";
configDir =
if pkgs.stdenv.isDarwin then
"Library/Application Support/org.dystroy.bacon"
else
"${config.xdg.configHome}/bacon";
in {
in
{
meta.maintainers = [ lib.hm.maintainers.shimunn ];
options.programs.bacon = {
@ -23,7 +30,13 @@ in {
default = { };
example = {
jobs.default = {
command = [ "cargo" "build" "--all-features" "--color" "always" ];
command = [
"cargo"
"build"
"--all-features"
"--color"
"always"
];
need_stdout = true;
};
};

View file

@ -1,10 +1,21 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkIf mkOption optionalAttrs types;
inherit (lib)
mkIf
mkOption
optionalAttrs
types
;
cfg = config.programs.bash;
writeBashScript = name: text:
writeBashScript =
name: text:
pkgs.writeTextFile {
inherit name text;
checkPhase = ''
@ -12,15 +23,19 @@ let
'';
};
in {
in
{
meta.maintainers = [ lib.maintainers.rycee ];
imports = [
(lib.mkRenamedOptionModule [ "programs" "bash" "enableAutojump" ] [
"programs"
"autojump"
"enable"
])
(lib.mkRenamedOptionModule
[ "programs" "bash" "enableAutojump" ]
[
"programs"
"autojump"
"enable"
]
)
];
options = {
@ -70,8 +85,14 @@ in {
};
historyControl = mkOption {
type = types.listOf
(types.enum [ "erasedups" "ignoredups" "ignorespace" "ignoreboth" ]);
type = types.listOf (
types.enum [
"erasedups"
"ignoredups"
"ignorespace"
"ignoreboth"
]
);
default = [ ];
description = "Controlling how commands are saved on the history list.";
};
@ -79,9 +100,12 @@ in {
historyIgnore = mkOption {
type = types.listOf types.str;
default = [ ];
example = [ "ls" "cd" "exit" ];
description =
"List of commands that should not be saved to the history list.";
example = [
"ls"
"cd"
"exit"
];
description = "List of commands that should not be saved to the history list.";
};
shellOptions = mkOption {
@ -101,7 +125,10 @@ in {
# Warn if closing shell with running jobs.
"checkjobs"
];
example = [ "extglob" "-cdspell" ];
example = [
"extglob"
"-cdspell"
];
description = ''
Shell options to set. Prefix an option with
"`-`" to unset.
@ -111,7 +138,9 @@ in {
sessionVariables = mkOption {
default = { };
type = types.attrs;
example = { MAILCHECK = 30; };
example = {
MAILCHECK = 30;
};
description = ''
Environment variables that will be set for the Bash session.
'';
@ -170,77 +199,90 @@ in {
};
};
config = let
aliasesStr = lib.concatStringsSep "\n"
(lib.mapAttrsToList (k: v: "alias ${k}=${lib.escapeShellArg v}")
cfg.shellAliases);
config =
let
aliasesStr = lib.concatStringsSep "\n" (
lib.mapAttrsToList (k: v: "alias ${k}=${lib.escapeShellArg v}") cfg.shellAliases
);
shoptsStr = let switch = v: if lib.hasPrefix "-" v then "-u" else "-s";
in lib.concatStringsSep "\n"
(map (v: "shopt ${switch v} ${lib.removePrefix "-" v}") cfg.shellOptions);
shoptsStr =
let
switch = v: if lib.hasPrefix "-" v then "-u" else "-s";
in
lib.concatStringsSep "\n" (map (v: "shopt ${switch v} ${lib.removePrefix "-" v}") cfg.shellOptions);
sessionVarsStr = config.lib.shell.exportAll cfg.sessionVariables;
sessionVarsStr = config.lib.shell.exportAll cfg.sessionVariables;
historyControlStr = (lib.concatStringsSep "\n"
(lib.mapAttrsToList (n: v: "${n}=${v}")
(optionalAttrs (cfg.historyFileSize != null) {
HISTFILESIZE = toString cfg.historyFileSize;
} // optionalAttrs (cfg.historySize != null) {
HISTSIZE = toString cfg.historySize;
} // optionalAttrs (cfg.historyFile != null) {
HISTFILE = ''"${cfg.historyFile}"'';
} // optionalAttrs (cfg.historyControl != [ ]) {
HISTCONTROL = lib.concatStringsSep ":" cfg.historyControl;
} // optionalAttrs (cfg.historyIgnore != [ ]) {
HISTIGNORE =
lib.escapeShellArg (lib.concatStringsSep ":" cfg.historyIgnore);
}) ++ lib.optional (cfg.historyFile != null)
''mkdir -p "$(dirname "$HISTFILE")"''));
in mkIf cfg.enable {
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
historyControlStr = (
lib.concatStringsSep "\n" (
lib.mapAttrsToList (n: v: "${n}=${v}") (
optionalAttrs (cfg.historyFileSize != null) {
HISTFILESIZE = toString cfg.historyFileSize;
}
// optionalAttrs (cfg.historySize != null) {
HISTSIZE = toString cfg.historySize;
}
// optionalAttrs (cfg.historyFile != null) {
HISTFILE = ''"${cfg.historyFile}"'';
}
// optionalAttrs (cfg.historyControl != [ ]) {
HISTCONTROL = lib.concatStringsSep ":" cfg.historyControl;
}
// optionalAttrs (cfg.historyIgnore != [ ]) {
HISTIGNORE = lib.escapeShellArg (lib.concatStringsSep ":" cfg.historyIgnore);
}
)
++ lib.optional (cfg.historyFile != null) ''mkdir -p "$(dirname "$HISTFILE")"''
)
);
in
mkIf cfg.enable {
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
home.file.".bash_profile".source = writeBashScript "bash_profile" ''
# include .profile if it exists
[[ -f ~/.profile ]] && . ~/.profile
home.file.".bash_profile".source = writeBashScript "bash_profile" ''
# include .profile if it exists
[[ -f ~/.profile ]] && . ~/.profile
# include .bashrc if it exists
[[ -f ~/.bashrc ]] && . ~/.bashrc
'';
# include .bashrc if it exists
[[ -f ~/.bashrc ]] && . ~/.bashrc
'';
# If completion is enabled then make sure it is sourced very early. This
# is to avoid problems if any other initialization code attempts to set up
# completion.
programs.bash.initExtra = mkIf cfg.enableCompletion (lib.mkOrder 100 ''
if [[ ! -v BASH_COMPLETION_VERSINFO ]]; then
. "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh"
fi
'');
# If completion is enabled then make sure it is sourced very early. This
# is to avoid problems if any other initialization code attempts to set up
# completion.
programs.bash.initExtra = mkIf cfg.enableCompletion (
lib.mkOrder 100 ''
if [[ ! -v BASH_COMPLETION_VERSINFO ]]; then
. "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh"
fi
''
);
home.file.".profile".source = writeBashScript "profile" ''
. "${config.home.profileDirectory}/etc/profile.d/hm-session-vars.sh"
home.file.".profile".source = writeBashScript "profile" ''
. "${config.home.profileDirectory}/etc/profile.d/hm-session-vars.sh"
${sessionVarsStr}
${sessionVarsStr}
${cfg.profileExtra}
'';
${cfg.profileExtra}
'';
home.file.".bashrc".source = writeBashScript "bashrc" ''
${cfg.bashrcExtra}
home.file.".bashrc".source = writeBashScript "bashrc" ''
${cfg.bashrcExtra}
# Commands that should be applied only for interactive shells.
[[ $- == *i* ]] || return
# Commands that should be applied only for interactive shells.
[[ $- == *i* ]] || return
${historyControlStr}
${historyControlStr}
${shoptsStr}
${shoptsStr}
${aliasesStr}
${aliasesStr}
${cfg.initExtra}
'';
${cfg.initExtra}
'';
home.file.".bash_logout" = mkIf (cfg.logoutExtra != "") {
source = writeBashScript "bash_logout" cfg.logoutExtra;
home.file.".bash_logout" = mkIf (cfg.logoutExtra != "") {
source = writeBashScript "bash_logout" cfg.logoutExtra;
};
};
};
}

View file

@ -1,6 +1,13 @@
{ config, lib, pkgs, ... }:
let cfg = config.programs.bashmount;
in {
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.bashmount;
in
{
meta.maintainers = [ lib.maintainers.AndersonTorres ];
options.programs.bashmount = {
@ -24,7 +31,6 @@ in {
config = lib.mkIf cfg.enable {
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
xdg.configFile."bashmount/config" =
lib.mkIf (cfg.extraConfig != "") { text = cfg.extraConfig; };
xdg.configFile."bashmount/config" = lib.mkIf (cfg.extraConfig != "") { text = cfg.extraConfig; };
};
}

View file

@ -1,10 +1,22 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) literalExpression mkEnableOption mkOption mkIf types;
inherit (lib)
literalExpression
mkEnableOption
mkOption
mkIf
types
;
cfg = config.programs.bat;
toConfigFile = attrs:
toConfigFile =
attrs:
let
inherit (builtins) isBool attrNames;
nonBoolFlags = lib.filterAttrs (_: v: !(isBool v)) attrs;
@ -17,20 +29,31 @@ let
switches = lib.concatMapStrings (k: ''
--${k}
'') (attrNames enabledBoolFlags);
in keyValuePairs + switches;
in {
in
keyValuePairs + switches;
in
{
meta.maintainers = with lib.maintainers; [ khaneliman ];
options.programs.bat = {
enable = mkEnableOption "bat, a cat clone with wings";
config = mkOption {
type = with types; attrsOf (oneOf [ str (listOf str) bool ]);
type =
with types;
attrsOf (oneOf [
str
(listOf str)
bool
]);
default = { };
example = {
theme = "TwoDark";
pager = "less -FR";
map-syntax = [ "*.jenkinsfile:Groovy" "*.props:Java Properties" ];
map-syntax = [
"*.jenkinsfile:Groovy"
"*.props:Java Properties"
];
};
description = ''
Bat configuration.
@ -40,8 +63,7 @@ in {
extraPackages = mkOption {
type = types.listOf types.package;
default = [ ];
example = literalExpression
"with pkgs.bat-extras; [ batdiff batman batgrep batwatch ];";
example = literalExpression "with pkgs.bat-extras; [ batdiff batman batgrep batwatch ];";
description = ''
Additional bat packages to install.
'';
@ -50,21 +72,24 @@ in {
package = lib.mkPackageOption pkgs "bat" { };
themes = mkOption {
type = types.attrsOf (types.either types.lines (types.submodule {
options = {
src = mkOption {
type = types.path;
description = "Path to the theme folder.";
};
type = types.attrsOf (
types.either types.lines (
types.submodule {
options = {
src = mkOption {
type = types.path;
description = "Path to the theme folder.";
};
file = mkOption {
type = types.nullOr types.str;
default = null;
description =
"Subpath of the theme file within the source, if needed.";
};
};
}));
file = mkOption {
type = types.nullOr types.str;
default = null;
description = "Subpath of the theme file within the source, if needed.";
};
};
}
)
);
default = { };
example = literalExpression ''
{
@ -85,20 +110,23 @@ in {
};
syntaxes = mkOption {
type = types.attrsOf (types.either types.lines (types.submodule {
options = {
src = mkOption {
type = types.path;
description = "Path to the syntax folder.";
};
file = mkOption {
type = types.nullOr types.str;
default = null;
description =
"Subpath of the syntax file within the source, if needed.";
};
};
}));
type = types.attrsOf (
types.either types.lines (
types.submodule {
options = {
src = mkOption {
type = types.path;
description = "Path to the syntax folder.";
};
file = mkOption {
type = types.nullOr types.str;
default = null;
description = "Subpath of the syntax file within the source, if needed.";
};
};
}
)
);
default = { };
example = literalExpression ''
{
@ -119,54 +147,75 @@ in {
};
};
config = mkIf cfg.enable (lib.mkMerge [
(mkIf (lib.any lib.isString (lib.attrValues cfg.themes)) {
warnings = [''
Using programs.bat.themes as a string option is deprecated and will be
removed in the future. Please change to using it as an attribute set
instead.
''];
})
(mkIf (lib.any lib.isString (lib.attrValues cfg.syntaxes)) {
warnings = [''
Using programs.bat.syntaxes as a string option is deprecated and will be
removed in the future. Please change to using it as an attribute set
instead.
''];
})
{
home.packages = [ cfg.package ] ++ cfg.extraPackages;
config = mkIf cfg.enable (
lib.mkMerge [
(mkIf (lib.any lib.isString (lib.attrValues cfg.themes)) {
warnings = [
''
Using programs.bat.themes as a string option is deprecated and will be
removed in the future. Please change to using it as an attribute set
instead.
''
];
})
(mkIf (lib.any lib.isString (lib.attrValues cfg.syntaxes)) {
warnings = [
''
Using programs.bat.syntaxes as a string option is deprecated and will be
removed in the future. Please change to using it as an attribute set
instead.
''
];
})
{
home.packages = [ cfg.package ] ++ cfg.extraPackages;
xdg.configFile = lib.mkMerge ([{
"bat/config" =
mkIf (cfg.config != { }) { text = toConfigFile cfg.config; };
}] ++ (lib.flip lib.mapAttrsToList cfg.themes (name: val: {
"bat/themes/${name}.tmTheme" = if lib.isString val then {
text = val;
} else {
source =
if isNull val.file then "${val.src}" else "${val.src}/${val.file}";
};
})) ++ (lib.flip lib.mapAttrsToList cfg.syntaxes (name: val: {
"bat/syntaxes/${name}.sublime-syntax" = if lib.isString val then {
text = val;
} else {
source =
if isNull val.file then "${val.src}" else "${val.src}/${val.file}";
};
})));
xdg.configFile = lib.mkMerge (
[
{
"bat/config" = mkIf (cfg.config != { }) { text = toConfigFile cfg.config; };
}
]
++ (lib.flip lib.mapAttrsToList cfg.themes (
name: val: {
"bat/themes/${name}.tmTheme" =
if lib.isString val then
{
text = val;
}
else
{
source = if isNull val.file then "${val.src}" else "${val.src}/${val.file}";
};
}
))
++ (lib.flip lib.mapAttrsToList cfg.syntaxes (
name: val: {
"bat/syntaxes/${name}.sublime-syntax" =
if lib.isString val then
{
text = val;
}
else
{
source = if isNull val.file then "${val.src}" else "${val.src}/${val.file}";
};
}
))
);
# NOTE: run `bat cache --build` in an empty directory to work
# around failure when ~/cache exists
# https://github.com/sharkdp/bat/issues/1726
home.activation.batCache = lib.hm.dag.entryAfter [ "linkGeneration" ] ''
(
export XDG_CACHE_HOME=${lib.escapeShellArg config.xdg.cacheHome}
verboseEcho "Rebuilding bat theme cache"
cd "${pkgs.emptyDirectory}"
run ${lib.getExe cfg.package} cache --build
)
'';
}
]);
# NOTE: run `bat cache --build` in an empty directory to work
# around failure when ~/cache exists
# https://github.com/sharkdp/bat/issues/1726
home.activation.batCache = lib.hm.dag.entryAfter [ "linkGeneration" ] ''
(
export XDG_CACHE_HOME=${lib.escapeShellArg config.xdg.cacheHome}
verboseEcho "Rebuilding bat theme cache"
cd "${pkgs.emptyDirectory}"
run ${lib.getExe cfg.package} cache --build
)
'';
}
]
);
}

View file

@ -1,22 +1,34 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) literalExpression mkIf mkOption types;
inherit (lib)
literalExpression
mkIf
mkOption
types
;
cfg = config.programs.beets;
yamlFormat = pkgs.formats.yaml { };
in {
meta.maintainers = with lib.maintainers; [ rycee Scrumplex ];
in
{
meta.maintainers = with lib.maintainers; [
rycee
Scrumplex
];
options = {
programs.beets = {
enable = mkOption {
type = types.bool;
default = if lib.versionAtLeast config.home.stateVersion "19.03" then
false
else
cfg.settings != { };
default =
if lib.versionAtLeast config.home.stateVersion "19.03" then false else cfg.settings != { };
defaultText = "false";
description = ''
Whether to enable the beets music library manager. This
@ -27,8 +39,7 @@ in {
};
package = lib.mkPackageOption pkgs "beets" {
example =
"(pkgs.beets.override { pluginOverrides = { beatport.enable = false; }; })";
example = "(pkgs.beets.override { pluginOverrides = { beatport.enable = false; }; })";
extraDescription = ''
Can be used to specify extensions.
'';
@ -70,8 +81,7 @@ in {
(mkIf cfg.enable {
home.packages = [ cfg.package ];
xdg.configFile."beets/config.yaml".source =
yamlFormat.generate "beets-config" cfg.settings;
xdg.configFile."beets/config.yaml".source = yamlFormat.generate "beets-config" cfg.settings;
})
(mkIf (cfg.mpdIntegration.enableStats || cfg.mpdIntegration.enableUpdate) {

View file

@ -1,6 +1,13 @@
{ config, lib, pkgs, ... }:
let cfg = config.programs.bemenu;
in {
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.bemenu;
in
{
meta.maintainers = [ ];
options.programs.bemenu = {
@ -9,7 +16,13 @@ in {
package = lib.mkPackageOption pkgs "bemenu" { nullable = true; };
settings = lib.mkOption {
type = with lib.types; attrsOf (oneOf [ str number bool ]);
type =
with lib.types;
attrsOf (oneOf [
str
number
bool
]);
default = { };
example = lib.literalExpression ''
{
@ -29,15 +42,13 @@ in {
width-factor = 0.3;
}
'';
description =
"Configuration options for bemenu. See {manpage}`bemenu(1)`.";
description = "Configuration options for bemenu. See {manpage}`bemenu(1)`.";
};
};
config = lib.mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "programs.bemenu" pkgs
lib.platforms.linux)
(lib.hm.assertions.assertPlatform "programs.bemenu" pkgs lib.platforms.linux)
];
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) literalExpression mkOption types;
@ -6,24 +11,33 @@ let
yamlFormat = pkgs.formats.yaml { };
mkNullableOption = args:
lib.mkOption (args // {
type = lib.types.nullOr args.type;
default = null;
});
mkNullableOption =
args:
lib.mkOption (
args
// {
type = lib.types.nullOr args.type;
default = null;
}
);
cleanRepositories = repos:
map (repo:
if builtins.isString repo then {
path = repo;
} else
removeNullValues repo) repos;
cleanRepositories =
repos:
map (
repo:
if builtins.isString repo then
{
path = repo;
}
else
removeNullValues repo
) repos;
mkRetentionOption = frequency:
mkRetentionOption =
frequency:
mkNullableOption {
type = types.int;
description =
"Number of ${frequency} archives to keep. Use -1 for no limit.";
description = "Number of ${frequency} archives to keep. Use -1 for no limit.";
example = 3;
};
@ -56,7 +70,12 @@ let
consistencyCheckModule = types.submodule {
options = {
name = mkOption {
type = types.enum [ "repository" "archives" "data" "extract" ];
type = types.enum [
"repository"
"archives"
"data"
"extract"
];
description = "Name of consistency check to run.";
example = "repository";
};
@ -69,146 +88,151 @@ let
};
};
configModule = types.submodule ({ config, ... }: {
config.location.extraConfig.exclude_from =
lib.mkIf config.location.excludeHomeManagerSymlinks
(lib.mkAfter [ (toString hmExcludeFile) ]);
options = {
location = {
sourceDirectories = mkNullableOption {
type = types.listOf types.str;
default = null;
description = ''
Directories to backup.
configModule = types.submodule (
{ config, ... }:
{
config.location.extraConfig.exclude_from = lib.mkIf config.location.excludeHomeManagerSymlinks (
lib.mkAfter [ (toString hmExcludeFile) ]
);
options = {
location = {
sourceDirectories = mkNullableOption {
type = types.listOf types.str;
default = null;
description = ''
Directories to backup.
Mutually exclusive with [](#opt-programs.borgmatic.backups._name_.location.patterns).
'';
example = literalExpression "[config.home.homeDirectory]";
Mutually exclusive with [](#opt-programs.borgmatic.backups._name_.location.patterns).
'';
example = literalExpression "[config.home.homeDirectory]";
};
patterns = mkNullableOption {
type = types.listOf types.str;
default = null;
description = ''
Patterns to include/exclude.
See the output of `borg help patterns` for the syntax. Pattern paths
are relative to `/` even when a different recursion root is set.
Mutually exclusive with [](#opt-programs.borgmatic.backups._name_.location.sourceDirectories).
'';
example = literalExpression ''
[
"R /home/user"
"- home/user/.cache"
"- home/user/Downloads"
"+ home/user/Videos/Important Video"
"- home/user/Videos"
]
'';
};
repositories = mkOption {
type = types.listOf (types.either types.str repositoryOption);
apply = cleanRepositories;
example = literalExpression ''
[
{
"path" = "ssh://myuser@myrepo.myserver.com/./repo";
"label" = "server";
}
{
"path" = "/var/lib/backups/local.borg";
"label" = "local";
}
]
'';
description = ''
List of local or remote repositories with paths and optional labels.
'';
};
excludeHomeManagerSymlinks = mkOption {
type = types.bool;
description = ''
Whether to exclude Home Manager generated symbolic links from
the backups. This facilitates restoring the whole home
directory when the Nix store doesn't contain the latest
Home Manager generation.
'';
default = false;
example = true;
};
extraConfig = extraConfigOption;
};
patterns = mkNullableOption {
type = types.listOf types.str;
default = null;
description = ''
Patterns to include/exclude.
See the output of `borg help patterns` for the syntax. Pattern paths
are relative to `/` even when a different recursion root is set.
Mutually exclusive with [](#opt-programs.borgmatic.backups._name_.location.sourceDirectories).
'';
example = literalExpression ''
[
"R /home/user"
"- home/user/.cache"
"- home/user/Downloads"
"+ home/user/Videos/Important Video"
"- home/user/Videos"
]
'';
storage = {
encryptionPasscommand = mkNullableOption {
type = types.str;
description = "Command writing the passphrase to standard output.";
example = literalExpression ''"''${pkgs.password-store}/bin/pass borg-repo"'';
};
extraConfig = extraConfigOption;
};
repositories = mkOption {
type = types.listOf (types.either types.str repositoryOption);
apply = cleanRepositories;
example = literalExpression ''
[
{
"path" = "ssh://myuser@myrepo.myserver.com/./repo";
"label" = "server";
}
{
"path" = "/var/lib/backups/local.borg";
"label" = "local";
}
]
'';
description = ''
List of local or remote repositories with paths and optional labels.
'';
retention = {
keepWithin = mkNullableOption {
type = types.strMatching "[[:digit:]]+[Hdwmy]";
description = "Keep all archives within this time interval.";
example = "2d";
};
keepSecondly = mkRetentionOption "secondly";
keepMinutely = mkRetentionOption "minutely";
keepHourly = mkRetentionOption "hourly";
keepDaily = mkRetentionOption "daily";
keepWeekly = mkRetentionOption "weekly";
keepMonthly = mkRetentionOption "monthly";
keepYearly = mkRetentionOption "yearly";
extraConfig = extraConfigOption;
};
excludeHomeManagerSymlinks = mkOption {
type = types.bool;
description = ''
Whether to exclude Home Manager generated symbolic links from
the backups. This facilitates restoring the whole home
directory when the Nix store doesn't contain the latest
Home Manager generation.
'';
default = false;
example = true;
consistency = {
checks = mkOption {
type = types.listOf consistencyCheckModule;
default = [ ];
description = "Consistency checks to run";
example = literalExpression ''
[
{
name = "repository";
frequency = "2 weeks";
}
{
name = "archives";
frequency = "4 weeks";
}
{
name = "data";
frequency = "6 weeks";
}
{
name = "extract";
frequency = "6 weeks";
}
];
'';
};
extraConfig = extraConfigOption;
};
extraConfig = extraConfigOption;
output = {
extraConfig = extraConfigOption;
};
hooks = {
extraConfig = extraConfigOption;
};
};
}
);
storage = {
encryptionPasscommand = mkNullableOption {
type = types.str;
description = "Command writing the passphrase to standard output.";
example =
literalExpression ''"''${pkgs.password-store}/bin/pass borg-repo"'';
};
extraConfig = extraConfigOption;
};
retention = {
keepWithin = mkNullableOption {
type = types.strMatching "[[:digit:]]+[Hdwmy]";
description = "Keep all archives within this time interval.";
example = "2d";
};
keepSecondly = mkRetentionOption "secondly";
keepMinutely = mkRetentionOption "minutely";
keepHourly = mkRetentionOption "hourly";
keepDaily = mkRetentionOption "daily";
keepWeekly = mkRetentionOption "weekly";
keepMonthly = mkRetentionOption "monthly";
keepYearly = mkRetentionOption "yearly";
extraConfig = extraConfigOption;
};
consistency = {
checks = mkOption {
type = types.listOf consistencyCheckModule;
default = [ ];
description = "Consistency checks to run";
example = literalExpression ''
[
{
name = "repository";
frequency = "2 weeks";
}
{
name = "archives";
frequency = "4 weeks";
}
{
name = "data";
frequency = "6 weeks";
}
{
name = "extract";
frequency = "6 weeks";
}
];
'';
};
extraConfig = extraConfigOption;
};
output = { extraConfig = extraConfigOption; };
hooks = { extraConfig = extraConfigOption; };
};
});
removeNullValues = attrSet:
lib.filterAttrs (key: value: value != null) attrSet;
removeNullValues = attrSet: lib.filterAttrs (key: value: value != null) attrSet;
hmFiles = builtins.attrValues config.home.file;
hmSymlinks = (lib.filter (file: !file.recursive) hmFiles);
@ -218,25 +242,35 @@ let
hmExcludePatterns = lib.concatMapStrings hmExcludePattern hmSymlinks;
hmExcludeFile = pkgs.writeText "hm-symlinks.txt" hmExcludePatterns;
writeConfig = config:
lib.generators.toYAML { } (removeNullValues ({
source_directories = config.location.sourceDirectories;
patterns = config.location.patterns;
repositories = config.location.repositories;
encryption_passcommand = config.storage.encryptionPasscommand;
keep_within = config.retention.keepWithin;
keep_secondly = config.retention.keepSecondly;
keep_minutely = config.retention.keepMinutely;
keep_hourly = config.retention.keepHourly;
keep_daily = config.retention.keepDaily;
keep_weekly = config.retention.keepWeekly;
keep_monthly = config.retention.keepMonthly;
keep_yearly = config.retention.keepYearly;
checks = config.consistency.checks;
} // config.location.extraConfig // config.storage.extraConfig
// config.retention.extraConfig // config.consistency.extraConfig
// config.output.extraConfig // config.hooks.extraConfig));
in {
writeConfig =
config:
lib.generators.toYAML { } (
removeNullValues (
{
source_directories = config.location.sourceDirectories;
patterns = config.location.patterns;
repositories = config.location.repositories;
encryption_passcommand = config.storage.encryptionPasscommand;
keep_within = config.retention.keepWithin;
keep_secondly = config.retention.keepSecondly;
keep_minutely = config.retention.keepMinutely;
keep_hourly = config.retention.keepHourly;
keep_daily = config.retention.keepDaily;
keep_weekly = config.retention.keepWeekly;
keep_monthly = config.retention.keepMonthly;
keep_yearly = config.retention.keepYearly;
checks = config.consistency.checks;
}
// config.location.extraConfig
// config.storage.extraConfig
// config.retention.extraConfig
// config.consistency.extraConfig
// config.output.extraConfig
// config.hooks.extraConfig
)
);
in
{
meta.maintainers = [ lib.maintainers.DamienCassou ];
options = {
@ -272,25 +306,28 @@ in {
};
config = lib.mkIf cfg.enable {
assertions = (lib.mapAttrsToList (backup: opts: {
assertion = opts.location.sourceDirectories == null
|| opts.location.patterns == null;
message = ''
Borgmatic backup configuration "${backup}" cannot specify both 'location.sourceDirectories' and 'location.patterns'.
'';
}) cfg.backups) ++ (lib.mapAttrsToList (backup: opts: {
assertion = !(opts.location.sourceDirectories == null
&& opts.location.patterns == null);
message = ''
Borgmatic backup configuration "${backup}" must specify one of 'location.sourceDirectories' or 'location.patterns'.
'';
}) cfg.backups);
assertions =
(lib.mapAttrsToList (backup: opts: {
assertion = opts.location.sourceDirectories == null || opts.location.patterns == null;
message = ''
Borgmatic backup configuration "${backup}" cannot specify both 'location.sourceDirectories' and 'location.patterns'.
'';
}) cfg.backups)
++ (lib.mapAttrsToList (backup: opts: {
assertion = !(opts.location.sourceDirectories == null && opts.location.patterns == null);
message = ''
Borgmatic backup configuration "${backup}" must specify one of 'location.sourceDirectories' or 'location.patterns'.
'';
}) cfg.backups);
xdg.configFile = with lib.attrsets;
mapAttrs' (configName: config:
xdg.configFile =
with lib.attrsets;
mapAttrs' (
configName: config:
nameValuePair ("borgmatic.d/" + configName + ".yaml") {
text = writeConfig config;
}) cfg.backups;
}
) cfg.backups;
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
};

View file

@ -1,11 +1,17 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.bottom;
tomlFormat = pkgs.formats.toml { };
in {
in
{
options = {
programs.bottom = {
enable = lib.mkEnableOption ''

View file

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) literalExpression mkOption types;
@ -37,7 +42,10 @@ let
};
mode = mkOption {
type = types.enum [ "file" "directory" ];
type = types.enum [
"file"
"directory"
];
default = "directory";
description = ''
Does the current path redirect a file or a directory?
@ -81,7 +89,8 @@ let
};
};
};
in {
in
{
options.programs.boxxy = {
enable = lib.mkEnableOption "boxxy: Boxes in badly behaving applications";
@ -96,13 +105,11 @@ in {
config = lib.mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "programs.boxxy" pkgs
lib.platforms.linux)
(lib.hm.assertions.assertPlatform "programs.boxxy" pkgs lib.platforms.linux)
];
home.file = lib.mkIf (cfg.rules != [ ]) {
"${configPath}".source =
settingsFormat.generate "boxxy-config.yaml" { rules = cfg.rules; };
"${configPath}".source = settingsFormat.generate "boxxy-config.yaml" { rules = cfg.rules; };
};
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
@ -110,4 +117,3 @@ in {
meta.maintainers = with lib.hm.maintainers; [ nikp123 ];
}

View file

@ -1,6 +1,17 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) literalExpression mkIf mkOption mkRenamedOptionModule types;
inherit (lib)
literalExpression
mkIf
mkOption
mkRenamedOptionModule
types
;
cfg = config.programs.broot;
@ -13,7 +24,15 @@ let
modal = lib.mkEnableOption "modal (vim) mode";
verbs = mkOption {
type = with types; listOf (attrsOf (oneOf [ bool str (listOf str) ]));
type =
with types;
listOf (
attrsOf (oneOf [
bool
str
(listOf str)
])
);
default = [ ];
example = literalExpression ''
[
@ -116,53 +135,64 @@ let
};
};
shellInit = shell:
shellInit =
shell:
# Using mkAfter to make it more likely to appear after other
# manipulations of the prompt.
lib.mkAfter ''
source ${
pkgs.runCommand "br.${shell}" { nativeBuildInputs = [ cfg.package ]; }
"broot --print-shell-function ${shell} > $out"
pkgs.runCommand "br.${shell}" {
nativeBuildInputs = [ cfg.package ];
} "broot --print-shell-function ${shell} > $out"
}
'';
in {
meta.maintainers = [ lib.hm.maintainers.aheaume lib.maintainers.dermetfan ];
in
{
meta.maintainers = [
lib.hm.maintainers.aheaume
lib.maintainers.dermetfan
];
imports = [
(mkRenamedOptionModule [ "programs" "broot" "modal" ] [
"programs"
"broot"
"settings"
"modal"
])
(mkRenamedOptionModule [ "programs" "broot" "verbs" ] [
"programs"
"broot"
"settings"
"verbs"
])
(mkRenamedOptionModule [ "programs" "broot" "skin" ] [
"programs"
"broot"
"settings"
"skin"
])
(mkRenamedOptionModule
[ "programs" "broot" "modal" ]
[
"programs"
"broot"
"settings"
"modal"
]
)
(mkRenamedOptionModule
[ "programs" "broot" "verbs" ]
[
"programs"
"broot"
"settings"
"verbs"
]
)
(mkRenamedOptionModule
[ "programs" "broot" "skin" ]
[
"programs"
"broot"
"settings"
"skin"
]
)
];
options.programs.broot = {
enable = lib.mkEnableOption "Broot, a better way to navigate directories";
enableBashIntegration =
lib.hm.shell.mkBashIntegrationOption { inherit config; };
enableBashIntegration = lib.hm.shell.mkBashIntegrationOption { inherit config; };
enableFishIntegration =
lib.hm.shell.mkFishIntegrationOption { inherit config; };
enableFishIntegration = lib.hm.shell.mkFishIntegrationOption { inherit config; };
enableNushellIntegration =
lib.hm.shell.mkNushellIntegrationOption { inherit config; };
enableNushellIntegration = lib.hm.shell.mkNushellIntegrationOption { inherit config; };
enableZshIntegration =
lib.hm.shell.mkZshIntegrationOption { inherit config; };
enableZshIntegration = lib.hm.shell.mkZshIntegrationOption { inherit config; };
package = lib.mkPackageOption pkgs "broot" { };
@ -190,9 +220,7 @@ in {
postBuild = ''
rm $out/conf.hjson
${lib.getExe pkgs.jq} --slurp add > $out/conf.hjson \
<(${
lib.getExe pkgs.hjson-go
} -c ${cfg.package.src}/resources/default-conf/conf.hjson) \
<(${lib.getExe pkgs.hjson-go} -c ${cfg.package.src}/resources/default-conf/conf.hjson) \
${jsonFormat.generate "broot-config.json" cfg.settings}
'';
};
@ -205,8 +233,7 @@ in {
fish.shellInit = mkIf cfg.enableFishIntegration (shellInit "fish");
nushell.extraConfig =
mkIf cfg.enableNushellIntegration (shellInit "nushell");
nushell.extraConfig = mkIf cfg.enableNushellIntegration (shellInit "nushell");
};
};
}

View file

@ -1,8 +1,21 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.browserpass;
browsers = [ "brave" "chrome" "chromium" "firefox" "librewolf" "vivaldi" ];
in {
browsers = [
"brave"
"chrome"
"chromium"
"firefox"
"librewolf"
"vivaldi"
];
in
{
options = {
programs.browserpass = {
enable = lib.mkEnableOption "the browserpass extension host application";
@ -17,82 +30,108 @@ in {
};
config = lib.mkIf cfg.enable {
home.file = lib.foldl' (a: b: a // b) { } (lib.concatMap (x:
with pkgs.stdenv;
if x == "brave" then
let
dir = if isDarwin then
"Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts"
else
".config/BraveSoftware/Brave-Browser/NativeMessagingHosts";
in [{
# Policies are read from `/etc/brave/policies` only
# https://github.com/brave/brave-browser/issues/19052
"${dir}/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/hosts/chromium/com.github.browserpass.native.json";
}]
else if x == "chrome" then
let
dir = if isDarwin then
"Library/Application Support/Google/Chrome/NativeMessagingHosts"
else
".config/google-chrome/NativeMessagingHosts";
in [{
"${dir}/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/hosts/chromium/com.github.browserpass.native.json";
"${dir}/../policies/managed/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/policies/chromium/com.github.browserpass.native.json";
}]
else if x == "chromium" then
let
dir = if isDarwin then
"Library/Application Support/Chromium/NativeMessagingHosts"
else
".config/chromium/NativeMessagingHosts";
in [
{
"${dir}/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/hosts/chromium/com.github.browserpass.native.json";
}
{
"${dir}/../policies/managed/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/policies/chromium/com.github.browserpass.native.json";
}
]
else if x == "firefox" then
let
dir = if isDarwin then
"Library/Application Support/Mozilla/NativeMessagingHosts"
else
".mozilla/native-messaging-hosts";
in [{
"${dir}/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/hosts/firefox/com.github.browserpass.native.json";
}]
else if x == "librewolf" then
let
dir = if isDarwin then
"Library/Application Support/LibreWolf/NativeMessagingHosts"
else
".librewolf/native-messaging-hosts";
in [{
"${dir}/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/hosts/firefox/com.github.browserpass.native.json";
}]
home.file = lib.foldl' (a: b: a // b) { } (
lib.concatMap (
x:
with pkgs.stdenv;
if x == "brave" then
let
dir =
if isDarwin then
"Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts"
else
".config/BraveSoftware/Brave-Browser/NativeMessagingHosts";
in
[
{
# Policies are read from `/etc/brave/policies` only
# https://github.com/brave/brave-browser/issues/19052
"${dir}/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/hosts/chromium/com.github.browserpass.native.json";
}
]
else if x == "chrome" then
let
dir =
if isDarwin then
"Library/Application Support/Google/Chrome/NativeMessagingHosts"
else
".config/google-chrome/NativeMessagingHosts";
in
[
{
"${dir}/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/hosts/chromium/com.github.browserpass.native.json";
"${dir}/../policies/managed/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/policies/chromium/com.github.browserpass.native.json";
}
]
else if x == "chromium" then
let
dir =
if isDarwin then
"Library/Application Support/Chromium/NativeMessagingHosts"
else
".config/chromium/NativeMessagingHosts";
in
[
{
"${dir}/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/hosts/chromium/com.github.browserpass.native.json";
}
{
"${dir}/../policies/managed/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/policies/chromium/com.github.browserpass.native.json";
}
]
else if x == "firefox" then
let
dir =
if isDarwin then
"Library/Application Support/Mozilla/NativeMessagingHosts"
else
".mozilla/native-messaging-hosts";
in
[
{
"${dir}/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/hosts/firefox/com.github.browserpass.native.json";
}
]
else if x == "librewolf" then
let
dir =
if isDarwin then
"Library/Application Support/LibreWolf/NativeMessagingHosts"
else
".librewolf/native-messaging-hosts";
in
[
{
"${dir}/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/hosts/firefox/com.github.browserpass.native.json";
}
]
else if x == "vivaldi" then
let
dir = if isDarwin then
"Library/Application Support/Vivaldi/NativeMessagingHosts"
else
".config/vivaldi/NativeMessagingHosts";
in [{
"${dir}/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/hosts/chromium/com.github.browserpass.native.json";
"${dir}/../policies/managed/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/policies/chromium/com.github.browserpass.native.json";
}]
else
throw "unknown browser ${x}") cfg.browsers);
else if x == "vivaldi" then
let
dir =
if isDarwin then
"Library/Application Support/Vivaldi/NativeMessagingHosts"
else
".config/vivaldi/NativeMessagingHosts";
in
[
{
"${dir}/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/hosts/chromium/com.github.browserpass.native.json";
"${dir}/../policies/managed/com.github.browserpass.native.json".source =
"${pkgs.browserpass}/lib/browserpass/policies/chromium/com.github.browserpass.native.json";
}
]
else
throw "unknown browser ${x}"
) cfg.browsers
);
};
}

Some files were not shown because too many files have changed in this diff Show more