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:
parent
5df48c4255
commit
cba2f9ce95
1051 changed files with 37028 additions and 26594 deletions
32
default.nix
32
default.nix
|
|
@ -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; };
|
||||
|
|
|
|||
232
docs/default.nix
232
docs/default.nix
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
82
flake.nix
82
flake.nix
|
|
@ -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;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
''
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
''
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
)
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
'');
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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 "[1;31mwarning: ${w}[0m" x;
|
||||
in lib.fold f res res.config.warnings;
|
||||
showWarnings =
|
||||
res:
|
||||
let
|
||||
f = w: x: builtins.trace "[1;31mwarning: ${w}[0m" 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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 ];
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
config = lib.mkIf (config.i18n.inputMethod.enabled == "hime") {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
config = lib.mkIf (config.i18n.inputMethod.enabled == "nabi") {
|
||||
|
|
|
|||
|
|
@ -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" ];
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
'';
|
||||
'';
|
||||
})
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}'';
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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 [ ];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
)
|
||||
);
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
'';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ])
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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
|
||||
'');
|
||||
''
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"}
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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"} "$@"
|
||||
''
|
||||
))
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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 { };
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
})
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" ];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
'';
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
'';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}";
|
||||
})
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = ""; }
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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
|
||||
'');
|
||||
''
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ]
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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 '\\\\' '\\'"
|
||||
];
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
'');
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
})
|
||||
]
|
||||
))
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ];
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; };
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
'';
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 ];
|
||||
|
|
|
|||
|
|
@ -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 ];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 ''
|
||||
|
|
|
|||
|
|
@ -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 ];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue