momo: NixOS 23.11, fix build (treefmt), move to flake-parts #262

Merged
teutat3s merged 12 commits from momo/nixos-23.11 into momo/main 2024-01-08 22:50:39 +00:00
20 changed files with 820 additions and 784 deletions
Showing only changes of commit 0212b85efc - Show all commits

View file

@ -36,8 +36,8 @@
erpnext.inputs.agenix.follows = "agenix"; erpnext.inputs.agenix.follows = "agenix";
}; };
outputs = inputs@{ self, ...}: outputs = inputs @ {self, ...}:
inputs.flake-parts.lib.mkFlake { inherit inputs; } { inputs.flake-parts.lib.mkFlake {inherit inputs;} {
debug = true; debug = true;
systems = [ systems = [
"x86_64-linux" "x86_64-linux"
@ -52,7 +52,12 @@
./overlays ./overlays
]; ];
perSystem = args@{ system, pkgs, config, ... }: { perSystem = args @ {
system,
pkgs,
config,
...
}: {
_module.args = { _module.args = {
inherit inputs; inherit inputs;
pkgs = import inputs.nixpkgs { pkgs = import inputs.nixpkgs {
@ -61,7 +66,7 @@
inputs.agenix.overlays.default inputs.agenix.overlays.default
]; ];
}; };
unstable = import inputs.unstable { inherit system; }; unstable = import inputs.unstable {inherit system;};
}; };
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {

View file

@ -1,5 +1,8 @@
{ self, inputs, ...}:
{ {
self,
inputs,
...
}: {
flake = { flake = {
nixosConfigurations = { nixosConfigurations = {
pioneer-momo-koeln = self.nixos-flake.lib.mkLinuxSystem { pioneer-momo-koeln = self.nixos-flake.lib.mkLinuxSystem {

View file

@ -1,4 +1,4 @@
{ ... }: { {...}: {
imports = [ imports = [
./configuration.nix ./configuration.nix
./hardware-configuration.nix ./hardware-configuration.nix

View file

@ -1,5 +1,4 @@
{ lib }: {lib}: hostnames: {
hostnames: {
"127.0.0.1" = hostnames; "127.0.0.1" = hostnames;
"::1" = hostnames; "::1" = hostnames;
} }

View file

@ -1,4 +1,8 @@
{ lib, inputs, ... }: { {
lib,
inputs,
...
}: {
# Configuration common to all Linux systems # Configuration common to all Linux systems
flake = { flake = {
lib = let lib = let
@ -10,9 +14,8 @@
#foo = callLibs ./foo.nix; #foo = callLibs ./foo.nix;
## In configs, they can be used under "lib.our" ## In configs, they can be used under "lib.our"
deploy = import ./deploy.nix { inherit inputs lib; }; deploy = import ./deploy.nix {inherit inputs lib;};
addLocalHostname = callLibs ./add-local-hostname.nix; addLocalHostname = callLibs ./add-local-hostname.nix;
recursiveMerge = callLibs ./recursive-merge.nix;
}; };
}; };
} }

View file

@ -1,11 +1,13 @@
/* /*
* The contents of this file are adapted from digga * The contents of this file are adapted from digga
* https://github.com/divnix/digga * https://github.com/divnix/digga
* *
* Licensed under the MIT license * Licensed under the MIT license
*/ */
{
{ lib, inputs }: let lib,
inputs,
}: let
getFqdn = c: let getFqdn = c: let
net = c.config.networking; net = c.config.networking;
fqdn = fqdn =
@ -17,35 +19,35 @@
in { in {
mkDeployNodes = systemConfigurations: extraConfig: mkDeployNodes = systemConfigurations: extraConfig:
/* /*
* *
Synopsis: mkNodes _systemConfigurations_ _extraConfig_ Synopsis: mkNodes _systemConfigurations_ _extraConfig_
Generate the `nodes` attribute expected by deploy-rs Generate the `nodes` attribute expected by deploy-rs
where _systemConfigurations_ are `nodes`. where _systemConfigurations_ are `nodes`.
_systemConfigurations_ should take the form of a flake's _systemConfigurations_ should take the form of a flake's
_nixosConfigurations_. Note that deploy-rs does not currently support _nixosConfigurations_. Note that deploy-rs does not currently support
deploying to darwin hosts. deploying to darwin hosts.
_extraConfig_, if specified, will be merged into each of the _extraConfig_, if specified, will be merged into each of the
nodes' configurations. nodes' configurations.
Example _systemConfigurations_ input: Example _systemConfigurations_ input:
``` ```
{ {
hostname-1 = { hostname-1 = {
fastConnection = true; fastConnection = true;
sshOpts = [ "-p" "25" ]; sshOpts = [ "-p" "25" ];
}; };
hostname-2 = { hostname-2 = {
sshOpts = [ "-p" "19999" ]; sshOpts = [ "-p" "19999" ];
sshUser = "root"; sshUser = "root";
}; };
} }
``` ```
* *
*/ */
lib.recursiveUpdate lib.recursiveUpdate
(lib.mapAttrs (lib.mapAttrs
( (

View file

@ -1,16 +0,0 @@
{ lib }:
attrList:
let
f = attrPath:
zipAttrsWith (
n: values:
if tail values == []
then head values
else if all isList values
then unique (concatLists values)
else if all isAttrs values
then f (attrPath ++ [n]) values
else last values
);
in
f [] attrList;

View file

@ -3,16 +3,24 @@
pkgs, pkgs,
lib, lib,
... ...
}: }: let
let
cfg = config.services.ddclient; cfg = config.services.ddclient;
boolToStr = bool: if bool then "yes" else "no"; boolToStr = bool:
if bool
then "yes"
else "no";
dataDir = "/var/lib/ddclient"; dataDir = "/var/lib/ddclient";
StateDirectory = builtins.baseNameOf dataDir; StateDirectory = builtins.baseNameOf dataDir;
RuntimeDirectory = StateDirectory; RuntimeDirectory = StateDirectory;
usev4 = if cfg.usev4 != "" then "usev4=${cfg.usev4}" else ""; usev4 =
usev6 = if cfg.usev6 != "" then "usev6=${cfg.usev6}" else ""; if cfg.usev4 != ""
then "usev4=${cfg.usev4}"
else "";
usev6 =
if cfg.usev6 != ""
then "usev6=${cfg.usev6}"
else "";
configFile' = pkgs.writeText "ddclient.conf" '' configFile' = pkgs.writeText "ddclient.conf" ''
# This file can be used as a template for configFile or is automatically generated by Nix options. # This file can be used as a template for configFile or is automatically generated by Nix options.
@ -22,11 +30,15 @@ let
cache=${dataDir}/ddclient.cache cache=${dataDir}/ddclient.cache
foreground=yes foreground=yes
login=${cfg.username} login=${cfg.username}
password=${if cfg.protocol == "nsupdate" then "/run/${RuntimeDirectory}/ddclient.key" else "@password_placeholder@"} password=${
if cfg.protocol == "nsupdate"
then "/run/${RuntimeDirectory}/ddclient.key"
else "@password_placeholder@"
}
protocol=${cfg.protocol} protocol=${cfg.protocol}
${lib.optionalString (cfg.script != "") "script=${cfg.script}"} ${lib.optionalString (cfg.script != "") "script=${cfg.script}"}
${lib.optionalString (cfg.server != "") "server=${cfg.server}"} ${lib.optionalString (cfg.server != "") "server=${cfg.server}"}
${lib.optionalString (cfg.zone != "") "zone=${cfg.zone}"} ${lib.optionalString (cfg.zone != "") "zone=${cfg.zone}"}
ssl=${boolToStr cfg.ssl} ssl=${boolToStr cfg.ssl}
wildcard=yes wildcard=yes
quiet=${boolToStr cfg.quiet} quiet=${boolToStr cfg.quiet}
@ -34,212 +46,224 @@ let
${cfg.extraConfig} ${cfg.extraConfig}
${lib.concatStringsSep "," cfg.domains} ${lib.concatStringsSep "," cfg.domains}
''; '';
configFile = if (cfg.configFile != null) then cfg.configFile else configFile'; configFile =
if (cfg.configFile != null)
then cfg.configFile
else configFile';
preStart = '' preStart = ''
install --mode=600 --owner=$USER ${configFile} /run/${RuntimeDirectory}/ddclient.conf install --mode=600 --owner=$USER ${configFile} /run/${RuntimeDirectory}/ddclient.conf
${lib.optionalString (cfg.configFile == null) (if (cfg.protocol == "nsupdate") then '' ${lib.optionalString (cfg.configFile == null) (
install --mode=600 --owner=$USER ${cfg.passwordFile} /run/${RuntimeDirectory}/ddclient.key if (cfg.protocol == "nsupdate")
'' else if (cfg.passwordFile != null) then '' then ''
"${pkgs.replace-secret}/bin/replace-secret" "@password_placeholder@" "${cfg.passwordFile}" "/run/${RuntimeDirectory}/ddclient.conf" install --mode=600 --owner=$USER ${cfg.passwordFile} /run/${RuntimeDirectory}/ddclient.key
'' else '' ''
sed -i '/^password=@password_placeholder@$/d' /run/${RuntimeDirectory}/ddclient.conf else if (cfg.passwordFile != null)
'')} then ''
"${pkgs.replace-secret}/bin/replace-secret" "@password_placeholder@" "${cfg.passwordFile}" "/run/${RuntimeDirectory}/ddclient.conf"
''
else ''
sed -i '/^password=@password_placeholder@$/d' /run/${RuntimeDirectory}/ddclient.conf
''
)}
''; '';
in with lib; { in
disabledModules = [ with lib; {
"services/networking/ddclient.nix" disabledModules = [
]; "services/networking/ddclient.nix"
];
imports = [ imports = [
(mkChangedOptionModule [ "services" "ddclient" "domain" ] [ "services" "ddclient" "domains" ] (mkChangedOptionModule ["services" "ddclient" "domain"] ["services" "ddclient" "domains"]
(config: (config: let
let value = getAttrFromPath [ "services" "ddclient" "domain" ] config; value = getAttrFromPath ["services" "ddclient" "domain"] config;
in if value != "" then [ value ] else [])) in
(mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "") if value != ""
(mkRemovedOptionModule [ "services" "ddclient" "password" ] "Use services.ddclient.passwordFile instead.") then [value]
]; else []))
(mkRemovedOptionModule ["services" "ddclient" "homeDir"] "")
(mkRemovedOptionModule ["services" "ddclient" "password"] "Use services.ddclient.passwordFile instead.")
];
###### interface ###### interface
options = { options = {
services.ddclient = with lib.types; { services.ddclient = with lib.types; {
enable = mkOption { enable = mkOption {
default = false; default = false;
type = bool; type = bool;
description = lib.mdDoc '' description = lib.mdDoc ''
Whether to synchronise your machine's IP address with a dynamic DNS provider (e.g. dyndns.org). Whether to synchronise your machine's IP address with a dynamic DNS provider (e.g. dyndns.org).
''; '';
}; };
package = mkOption { package = mkOption {
type = package; type = package;
default = pkgs.ddclient; default = pkgs.ddclient;
defaultText = lib.literalExpression "pkgs.ddclient"; defaultText = lib.literalExpression "pkgs.ddclient";
description = lib.mdDoc '' description = lib.mdDoc ''
The ddclient executable package run by the service. The ddclient executable package run by the service.
''; '';
}; };
domains = mkOption { domains = mkOption {
default = [ "" ]; default = [""];
type = listOf str; type = listOf str;
description = lib.mdDoc '' description = lib.mdDoc ''
Domain name(s) to synchronize. Domain name(s) to synchronize.
''; '';
}; };
username = mkOption { username = mkOption {
# For `nsupdate` username contains the path to the nsupdate executable # For `nsupdate` username contains the path to the nsupdate executable
default = lib.optionalString (config.services.ddclient.protocol == "nsupdate") "${pkgs.bind.dnsutils}/bin/nsupdate"; default = lib.optionalString (config.services.ddclient.protocol == "nsupdate") "${pkgs.bind.dnsutils}/bin/nsupdate";
defaultText = ""; defaultText = "";
type = str; type = str;
description = lib.mdDoc '' description = lib.mdDoc ''
User name. User name.
''; '';
}; };
passwordFile = mkOption { passwordFile = mkOption {
default = null; default = null;
type = nullOr str; type = nullOr str;
description = lib.mdDoc '' description = lib.mdDoc ''
A file containing the password or a TSIG key in named format when using the nsupdate protocol. A file containing the password or a TSIG key in named format when using the nsupdate protocol.
''; '';
}; };
interval = mkOption { interval = mkOption {
default = "10min"; default = "10min";
type = str; type = str;
description = lib.mdDoc '' description = lib.mdDoc ''
The interval at which to run the check and update. The interval at which to run the check and update.
See {command}`man 7 systemd.time` for the format. See {command}`man 7 systemd.time` for the format.
''; '';
}; };
configFile = mkOption { configFile = mkOption {
default = null; default = null;
type = nullOr path; type = nullOr path;
description = lib.mdDoc '' description = lib.mdDoc ''
Path to configuration file. Path to configuration file.
When set this overrides the generated configuration from module options. When set this overrides the generated configuration from module options.
''; '';
example = "/root/nixos/secrets/ddclient.conf"; example = "/root/nixos/secrets/ddclient.conf";
}; };
protocol = mkOption { protocol = mkOption {
default = "dyndns2"; default = "dyndns2";
type = str; type = str;
description = lib.mdDoc '' description = lib.mdDoc ''
Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols). Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols).
''; '';
}; };
server = mkOption { server = mkOption {
default = ""; default = "";
type = str; type = str;
description = lib.mdDoc '' description = lib.mdDoc ''
Server address. Server address.
''; '';
}; };
ssl = mkOption { ssl = mkOption {
default = true; default = true;
type = bool; type = bool;
description = lib.mdDoc '' description = lib.mdDoc ''
Whether to use SSL/TLS to connect to dynamic DNS provider. Whether to use SSL/TLS to connect to dynamic DNS provider.
''; '';
}; };
quiet = mkOption { quiet = mkOption {
default = false; default = false;
type = bool; type = bool;
description = lib.mdDoc '' description = lib.mdDoc ''
Print no messages for unnecessary updates. Print no messages for unnecessary updates.
''; '';
}; };
script = mkOption { script = mkOption {
default = ""; default = "";
type = str; type = str;
description = lib.mdDoc '' description = lib.mdDoc ''
script as required by some providers. script as required by some providers.
''; '';
}; };
usev4 = mkOption { usev4 = mkOption {
default = "webv4, webv4=checkip.dyndns.com/, webv4-skip='Current IP Address: '"; default = "webv4, webv4=checkip.dyndns.com/, webv4-skip='Current IP Address: '";
type = str; type = str;
description = lib.mdDoc '' description = lib.mdDoc ''
Method to determine the IP address to send to the dynamic DNS provider. Method to determine the IP address to send to the dynamic DNS provider.
''; '';
}; };
usev6 = mkOption { usev6 = mkOption {
default = ""; default = "";
type = str; type = str;
description = lib.mdDoc '' description = lib.mdDoc ''
Method to determine the IP address to send to the dynamic DNS provider. Method to determine the IP address to send to the dynamic DNS provider.
''; '';
}; };
verbose = mkOption { verbose = mkOption {
default = false; default = false;
type = bool; type = bool;
description = lib.mdDoc '' description = lib.mdDoc ''
Print verbose information. Print verbose information.
''; '';
}; };
zone = mkOption { zone = mkOption {
default = ""; default = "";
type = str; type = str;
description = lib.mdDoc '' description = lib.mdDoc ''
zone as required by some providers. zone as required by some providers.
''; '';
}; };
extraConfig = mkOption { extraConfig = mkOption {
default = ""; default = "";
type = lines; type = lines;
description = lib.mdDoc '' description = lib.mdDoc ''
Extra configuration. Contents will be added verbatim to the configuration file. Extra configuration. Contents will be added verbatim to the configuration file.
::: {.note} ::: {.note}
`daemon` should not be added here because it does not work great with the systemd-timer approach the service uses. `daemon` should not be added here because it does not work great with the systemd-timer approach the service uses.
::: :::
''; '';
}; };
};
};
###### implementation
config = mkIf config.services.ddclient.enable {
systemd.services.ddclient = {
description = "Dynamic DNS Client";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
restartTriggers = optional (cfg.configFile != null) cfg.configFile;
serviceConfig = {
DynamicUser = true;
RuntimeDirectoryMode = "0700";
inherit RuntimeDirectory;
inherit StateDirectory;
Type = "oneshot";
ExecStartPre = "!${pkgs.writeShellScript "ddclient-prestart" preStart}";
ExecStart = "${lib.getBin cfg.package}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf";
}; };
}; };
systemd.timers.ddclient = { ###### implementation
description = "Run ddclient";
wantedBy = [ "timers.target" ]; config = mkIf config.services.ddclient.enable {
timerConfig = { systemd.services.ddclient = {
OnBootSec = cfg.interval; description = "Dynamic DNS Client";
OnUnitInactiveSec = cfg.interval; wantedBy = ["multi-user.target"];
after = ["network.target"];
restartTriggers = optional (cfg.configFile != null) cfg.configFile;
serviceConfig = {
DynamicUser = true;
RuntimeDirectoryMode = "0700";
inherit RuntimeDirectory;
inherit StateDirectory;
Type = "oneshot";
ExecStartPre = "!${pkgs.writeShellScript "ddclient-prestart" preStart}";
ExecStart = "${lib.getBin cfg.package}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf";
};
};
systemd.timers.ddclient = {
description = "Run ddclient";
wantedBy = ["timers.target"];
timerConfig = {
OnBootSec = cfg.interval;
OnUnitInactiveSec = cfg.interval;
};
}; };
}; };
}; }
}

View file

@ -29,13 +29,13 @@
else "" else ""
) )
+ '' + ''
bindsym l exec ${pkgs.swaylock-bg}/bin/swaylock-bg, mode "default" bindsym l exec ${pkgs.swaylock-bg}/bin/swaylock-bg, mode "default"
bindsym s exec systemctl suspend, mode "default" bindsym s exec systemctl suspend, mode "default"
bindsym r exec systemctl reboot, mode "default" bindsym r exec systemctl reboot, mode "default"
bindsym Shift+s exec systemctl poweroff, mode "default" bindsym Shift+s exec systemctl poweroff, mode "default"
# exit system mode: "Enter" or "Escape" # exit system mode: "Enter" or "Escape"
bindsym Return mode "default" bindsym Return mode "default"
bindsym Escape mode "default" bindsym Escape mode "default"
} }
'' ''

View file

@ -24,9 +24,10 @@ in {
''; '';
}; };
environment.systemPackages = with pkgs; mkIf psCfg.graphical.v4l2loopback.enable [ environment.systemPackages = with pkgs;
linuxPackages.v4l2loopback mkIf psCfg.graphical.v4l2loopback.enable [
]; linuxPackages.v4l2loopback
];
programs.sway.enable = true; programs.sway.enable = true;

View file

@ -1,215 +1,221 @@
{ config, pkgs, lib, ... }: {
config,
with lib; pkgs,
lib,
let ...
}:
with lib; let
cfg = config.services.invoiceplane; cfg = config.services.invoiceplane;
eachSite = cfg.sites; eachSite = cfg.sites;
user = "invoiceplane"; user = "invoiceplane";
webserver = config.services.${cfg.webserver}; webserver = config.services.${cfg.webserver};
invoiceplane-config = hostName: cfg: pkgs.writeText "ipconfig.php" '' invoiceplane-config = hostName: cfg:
IP_URL=http://${hostName} pkgs.writeText "ipconfig.php" ''
ENABLE_DEBUG=false IP_URL=http://${hostName}
DISABLE_SETUP=false ENABLE_DEBUG=false
REMOVE_INDEXPHP=false DISABLE_SETUP=false
DB_HOSTNAME=${cfg.database.host} REMOVE_INDEXPHP=false
DB_USERNAME=${cfg.database.user} DB_HOSTNAME=${cfg.database.host}
# NOTE: file_get_contents adds newline at the end of returned string DB_USERNAME=${cfg.database.user}
DB_PASSWORD=${if cfg.database.passwordFile == null then "" else "trim(file_get_contents('${cfg.database.passwordFile}'),\"\\r\\n\")"} # NOTE: file_get_contents adds newline at the end of returned string
DB_DATABASE=${cfg.database.name} DB_PASSWORD=${
DB_PORT=${toString cfg.database.port} if cfg.database.passwordFile == null
SESS_EXPIRATION=864000 then ""
ENABLE_INVOICE_DELETION=false else "trim(file_get_contents('${cfg.database.passwordFile}'),\"\\r\\n\")"
DISABLE_READ_ONLY=false }
ENCRYPTION_KEY= DB_DATABASE=${cfg.database.name}
ENCRYPTION_CIPHER=AES-256 DB_PORT=${toString cfg.database.port}
SETUP_COMPLETED=false SESS_EXPIRATION=864000
REMOVE_INDEXPHP=true ENABLE_INVOICE_DELETION=false
''; DISABLE_READ_ONLY=false
ENCRYPTION_KEY=
extraConfig = hostName: cfg: pkgs.writeText "extraConfig.php" '' ENCRYPTION_CIPHER=AES-256
${toString cfg.extraConfig} SETUP_COMPLETED=false
''; REMOVE_INDEXPHP=true
pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec {
pname = "invoiceplane-${hostName}";
version = src.version;
src = pkgs.invoiceplane;
postPhase = ''
# Patch index.php file to load additional config file
substituteInPlace index.php \
--replace "require('vendor/autoload.php');" "require('vendor/autoload.php'); \$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, 'extraConfig.php'); \$dotenv->load();";
''; '';
installPhase = '' extraConfig = hostName: cfg:
mkdir -p $out pkgs.writeText "extraConfig.php" ''
cp -r * $out/ ${toString cfg.extraConfig}
# symlink uploads and log directories
rm -r $out/uploads $out/application/logs $out/vendor/mpdf/mpdf/tmp
ln -sf ${cfg.stateDir}/uploads $out/
ln -sf ${cfg.stateDir}/logs $out/application/
ln -sf ${cfg.stateDir}/tmp $out/vendor/mpdf/mpdf/
# symlink the InvoicePlane config
ln -s ${cfg.stateDir}/ipconfig.php $out/ipconfig.php
# symlink the extraConfig file
ln -s ${extraConfig hostName cfg} $out/extraConfig.php
# symlink additional templates
${concatMapStringsSep "\n" (template: "cp -r ${template}/. $out/application/views/invoice_templates/pdf/") cfg.invoiceTemplates}
''; '';
};
siteOpts = { lib, name, ... }: pkg = hostName: cfg:
{ pkgs.stdenv.mkDerivation rec {
options = { pname = "invoiceplane-${hostName}";
version = src.version;
src = pkgs.invoiceplane;
enable = mkEnableOption (lib.mdDoc "InvoicePlane web application"); postPhase = ''
# Patch index.php file to load additional config file
substituteInPlace index.php \
--replace "require('vendor/autoload.php');" "require('vendor/autoload.php'); \$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, 'extraConfig.php'); \$dotenv->load();";
'';
stateDir = mkOption { installPhase = ''
type = types.path; mkdir -p $out
default = "/var/lib/invoiceplane/${name}"; cp -r * $out/
description = lib.mdDoc ''
This directory is used for uploads of attachments and cache.
The directory passed here is automatically created and permissions
adjusted as required.
'';
};
database = { # symlink uploads and log directories
host = mkOption { rm -r $out/uploads $out/application/logs $out/vendor/mpdf/mpdf/tmp
type = types.str; ln -sf ${cfg.stateDir}/uploads $out/
default = "localhost"; ln -sf ${cfg.stateDir}/logs $out/application/
description = lib.mdDoc "Database host address."; ln -sf ${cfg.stateDir}/tmp $out/vendor/mpdf/mpdf/
};
port = mkOption { # symlink the InvoicePlane config
type = types.port; ln -s ${cfg.stateDir}/ipconfig.php $out/ipconfig.php
default = 3306;
description = lib.mdDoc "Database host port.";
};
name = mkOption { # symlink the extraConfig file
type = types.str; ln -s ${extraConfig hostName cfg} $out/extraConfig.php
default = "invoiceplane";
description = lib.mdDoc "Database name.";
};
user = mkOption { # symlink additional templates
type = types.str; ${concatMapStringsSep "\n" (template: "cp -r ${template}/. $out/application/views/invoice_templates/pdf/") cfg.invoiceTemplates}
default = "invoiceplane"; '';
description = lib.mdDoc "Database user."; };
};
passwordFile = mkOption { siteOpts = {
type = types.nullOr types.path; lib,
default = null; name,
example = "/run/keys/invoiceplane-dbpassword"; ...
description = lib.mdDoc '' }: {
A file containing the password corresponding to options = {
{option}`database.user`. enable = mkEnableOption (lib.mdDoc "InvoicePlane web application");
'';
};
createLocally = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc "Create the database and database user locally.";
};
};
invoiceTemplates = mkOption {
type = types.listOf types.path;
default = [];
description = lib.mdDoc ''
List of path(s) to respective template(s) which are copied from the 'invoice_templates/pdf' directory.
::: {.note}
These templates need to be packaged before use, see example.
:::
'';
example = literalExpression ''
let
# Let's package an example template
template-vtdirektmarketing = pkgs.stdenv.mkDerivation {
name = "vtdirektmarketing";
# Download the template from a public repository
src = pkgs.fetchgit {
url = "https://git.project-insanity.org/onny/invoiceplane-vtdirektmarketing.git";
sha256 = "1hh0q7wzsh8v8x03i82p6qrgbxr4v5fb05xylyrpp975l8axyg2z";
};
sourceRoot = ".";
# Installing simply means copying template php file to the output directory
installPhase = ""
mkdir -p $out
cp invoiceplane-vtdirektmarketing/vtdirektmarketing.php $out/
"";
};
# And then pass this package to the template list like this:
in [ template-vtdirektmarketing ]
'';
};
poolConfig = mkOption {
type = with types; attrsOf (oneOf [ str int bool ]);
default = {
"pm" = "dynamic";
"pm.max_children" = 32;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 2;
"pm.max_spare_servers" = 4;
"pm.max_requests" = 500;
};
description = lib.mdDoc ''
Options for the InvoicePlane PHP pool. See the documentation on `php-fpm.conf`
for details on configuration directives.
'';
};
extraConfig = mkOption {
type = types.nullOr types.lines;
default = null;
example = ''
SETUP_COMPLETED=true
DISABLE_SETUP=true
IP_URL=https://invoice.example.com
'';
description = lib.mdDoc ''
InvoicePlane configuration. Refer to
<https://github.com/InvoicePlane/InvoicePlane/blob/master/ipconfig.php.example>
for details on supported values.
'';
};
cron = {
enable = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Enable cron service which periodically runs Invoiceplane tasks.
Requires key taken from the administration page. Refer to
<https://wiki.invoiceplane.com/en/1.0/modules/recurring-invoices>
on how to configure it.
'';
};
key = mkOption {
type = types.str;
description = lib.mdDoc "Cron key taken from the administration page.";
};
};
stateDir = mkOption {
type = types.path;
default = "/var/lib/invoiceplane/${name}";
description = lib.mdDoc ''
This directory is used for uploads of attachments and cache.
The directory passed here is automatically created and permissions
adjusted as required.
'';
}; };
database = {
host = mkOption {
type = types.str;
default = "localhost";
description = lib.mdDoc "Database host address.";
};
port = mkOption {
type = types.port;
default = 3306;
description = lib.mdDoc "Database host port.";
};
name = mkOption {
type = types.str;
default = "invoiceplane";
description = lib.mdDoc "Database name.";
};
user = mkOption {
type = types.str;
default = "invoiceplane";
description = lib.mdDoc "Database user.";
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/keys/invoiceplane-dbpassword";
description = lib.mdDoc ''
A file containing the password corresponding to
{option}`database.user`.
'';
};
createLocally = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc "Create the database and database user locally.";
};
};
invoiceTemplates = mkOption {
type = types.listOf types.path;
default = [];
description = lib.mdDoc ''
List of path(s) to respective template(s) which are copied from the 'invoice_templates/pdf' directory.
::: {.note}
These templates need to be packaged before use, see example.
:::
'';
example = literalExpression ''
let
# Let's package an example template
template-vtdirektmarketing = pkgs.stdenv.mkDerivation {
name = "vtdirektmarketing";
# Download the template from a public repository
src = pkgs.fetchgit {
url = "https://git.project-insanity.org/onny/invoiceplane-vtdirektmarketing.git";
sha256 = "1hh0q7wzsh8v8x03i82p6qrgbxr4v5fb05xylyrpp975l8axyg2z";
};
sourceRoot = ".";
# Installing simply means copying template php file to the output directory
installPhase = ""
mkdir -p $out
cp invoiceplane-vtdirektmarketing/vtdirektmarketing.php $out/
"";
};
# And then pass this package to the template list like this:
in [ template-vtdirektmarketing ]
'';
};
poolConfig = mkOption {
type = with types; attrsOf (oneOf [str int bool]);
default = {
"pm" = "dynamic";
"pm.max_children" = 32;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 2;
"pm.max_spare_servers" = 4;
"pm.max_requests" = 500;
};
description = lib.mdDoc ''
Options for the InvoicePlane PHP pool. See the documentation on `php-fpm.conf`
for details on configuration directives.
'';
};
extraConfig = mkOption {
type = types.nullOr types.lines;
default = null;
example = ''
SETUP_COMPLETED=true
DISABLE_SETUP=true
IP_URL=https://invoice.example.com
'';
description = lib.mdDoc ''
InvoicePlane configuration. Refer to
<https://github.com/InvoicePlane/InvoicePlane/blob/master/ipconfig.php.example>
for details on supported values.
'';
};
cron = {
enable = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Enable cron service which periodically runs Invoiceplane tasks.
Requires key taken from the administration page. Refer to
<https://wiki.invoiceplane.com/en/1.0/modules/recurring-invoices>
on how to configure it.
'';
};
key = mkOption {
type = types.str;
description = lib.mdDoc "Cron key taken from the administration page.";
};
};
}; };
in };
{ in {
disabledModules = [ disabledModules = [
"services/web-apps/invoiceplane.nix" "services/web-apps/invoiceplane.nix"
]; ];
@ -218,7 +224,6 @@ in
options = { options = {
services.invoiceplane = mkOption { services.invoiceplane = mkOption {
type = types.submodule { type = types.submodule {
options.sites = mkOption { options.sites = mkOption {
type = types.attrsOf (types.submodule siteOpts); type = types.attrsOf (types.submodule siteOpts);
default = {}; default = {};
@ -226,7 +231,7 @@ in
}; };
options.webserver = mkOption { options.webserver = mkOption {
type = types.enum [ "caddy" ]; type = types.enum ["caddy"];
default = "caddy"; default = "caddy";
description = lib.mdDoc '' description = lib.mdDoc ''
Which webserver to use for virtual host management. Currently only Which webserver to use for virtual host management. Currently only
@ -237,126 +242,136 @@ in
default = {}; default = {};
description = lib.mdDoc "InvoicePlane configuration."; description = lib.mdDoc "InvoicePlane configuration.";
}; };
}; };
# implementation # implementation
config = mkIf (eachSite != {}) (mkMerge [{ config = mkIf (eachSite != {}) (mkMerge [
{
assertions = flatten (mapAttrsToList (hostName: cfg: [
{
assertion = cfg.database.createLocally -> cfg.database.user == user;
message = ''services.invoiceplane.sites."${hostName}".database.user must be ${user} if the database is to be automatically provisioned'';
}
{
assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
message = ''services.invoiceplane.sites."${hostName}".database.passwordFile cannot be specified if services.invoiceplane.sites."${hostName}".database.createLocally is set to true.'';
}
{
assertion = cfg.cron.enable -> cfg.cron.key != null;
message = ''services.invoiceplane.sites."${hostName}".cron.key must be set in order to use cron service.'';
}
])
eachSite);
assertions = flatten (mapAttrsToList (hostName: cfg: services.mysql = mkIf (any (v: v.database.createLocally) (attrValues eachSite)) {
[{ assertion = cfg.database.createLocally -> cfg.database.user == user; enable = true;
message = ''services.invoiceplane.sites."${hostName}".database.user must be ${user} if the database is to be automatically provisioned''; package = mkDefault pkgs.mariadb;
} ensureDatabases = mapAttrsToList (hostName: cfg: cfg.database.name) eachSite;
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; ensureUsers =
message = ''services.invoiceplane.sites."${hostName}".database.passwordFile cannot be specified if services.invoiceplane.sites."${hostName}".database.createLocally is set to true.''; mapAttrsToList (
} hostName: cfg: {
{ assertion = cfg.cron.enable -> cfg.cron.key != null; name = cfg.database.user;
message = ''services.invoiceplane.sites."${hostName}".cron.key must be set in order to use cron service.''; ensurePermissions = {"${cfg.database.name}.*" = "ALL PRIVILEGES";};
} }
]) eachSite); )
eachSite;
};
services.mysql = mkIf (any (v: v.database.createLocally) (attrValues eachSite)) { services.phpfpm = {
enable = true; phpPackage = pkgs.php81;
package = mkDefault pkgs.mariadb; pools =
ensureDatabases = mapAttrsToList (hostName: cfg: cfg.database.name) eachSite; mapAttrs' (hostName: cfg: (
ensureUsers = mapAttrsToList (hostName: cfg: nameValuePair "invoiceplane-${hostName}" {
{ name = cfg.database.user; inherit user;
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }; group = webserver.group;
} settings =
) eachSite; {
}; "listen.owner" = webserver.user;
"listen.group" = webserver.group;
}
// cfg.poolConfig;
}
))
eachSite;
};
}
services.phpfpm = { {
phpPackage = pkgs.php81; systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
pools = mapAttrs' (hostName: cfg: ( "d ${cfg.stateDir} 0750 ${user} ${webserver.group} - -"
nameValuePair "invoiceplane-${hostName}" { "f ${cfg.stateDir}/ipconfig.php 0750 ${user} ${webserver.group} - -"
inherit user; "d ${cfg.stateDir}/logs 0750 ${user} ${webserver.group} - -"
group = webserver.group; "d ${cfg.stateDir}/uploads 0750 ${user} ${webserver.group} - -"
settings = { "d ${cfg.stateDir}/uploads/archive 0750 ${user} ${webserver.group} - -"
"listen.owner" = webserver.user; "d ${cfg.stateDir}/uploads/customer_files 0750 ${user} ${webserver.group} - -"
"listen.group" = webserver.group; "d ${cfg.stateDir}/uploads/temp 0750 ${user} ${webserver.group} - -"
} // cfg.poolConfig; "d ${cfg.stateDir}/uploads/temp/mpdf 0750 ${user} ${webserver.group} - -"
} "d ${cfg.stateDir}/tmp 0750 ${user} ${webserver.group} - -"
)) eachSite; ])
}; eachSite);
} systemd.services.invoiceplane-config = {
serviceConfig.Type = "oneshot";
script = concatStrings (mapAttrsToList (hostName: cfg: ''
mkdir -p ${cfg.stateDir}/logs \
${cfg.stateDir}/uploads
if ! grep -q IP_URL "${cfg.stateDir}/ipconfig.php"; then
cp "${invoiceplane-config hostName cfg}" "${cfg.stateDir}/ipconfig.php"
fi
'')
eachSite);
wantedBy = ["multi-user.target"];
};
{ users.users.${user} = {
group = webserver.group;
isSystemUser = true;
};
}
{
# Cron service implementation
systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [ systemd.timers =
"d ${cfg.stateDir} 0750 ${user} ${webserver.group} - -" mapAttrs' (hostName: cfg: (
"f ${cfg.stateDir}/ipconfig.php 0750 ${user} ${webserver.group} - -" nameValuePair "invoiceplane-cron-${hostName}" (mkIf cfg.cron.enable {
"d ${cfg.stateDir}/logs 0750 ${user} ${webserver.group} - -" wantedBy = ["timers.target"];
"d ${cfg.stateDir}/uploads 0750 ${user} ${webserver.group} - -" timerConfig = {
"d ${cfg.stateDir}/uploads/archive 0750 ${user} ${webserver.group} - -" OnBootSec = "5m";
"d ${cfg.stateDir}/uploads/customer_files 0750 ${user} ${webserver.group} - -" OnUnitActiveSec = "5m";
"d ${cfg.stateDir}/uploads/temp 0750 ${user} ${webserver.group} - -" Unit = "invoiceplane-cron-${hostName}.service";
"d ${cfg.stateDir}/uploads/temp/mpdf 0750 ${user} ${webserver.group} - -" };
"d ${cfg.stateDir}/tmp 0750 ${user} ${webserver.group} - -" })
]) eachSite); ))
eachSite;
systemd.services.invoiceplane-config = { systemd.services =
serviceConfig.Type = "oneshot"; mapAttrs' (hostName: cfg: (
script = concatStrings (mapAttrsToList (hostName: cfg: nameValuePair "invoiceplane-cron-${hostName}" (mkIf cfg.cron.enable {
'' serviceConfig = {
mkdir -p ${cfg.stateDir}/logs \ Type = "oneshot";
${cfg.stateDir}/uploads User = user;
if ! grep -q IP_URL "${cfg.stateDir}/ipconfig.php"; then ExecStart = "${pkgs.curl}/bin/curl --header 'Host: ${hostName}' http://localhost/invoices/cron/recur/${cfg.cron.key}";
cp "${invoiceplane-config hostName cfg}" "${cfg.stateDir}/ipconfig.php" };
fi })
'') eachSite); ))
wantedBy = [ "multi-user.target" ]; eachSite;
}; }
users.users.${user} = {
group = webserver.group;
isSystemUser = true;
};
}
{
# Cron service implementation
systemd.timers = mapAttrs' (hostName: cfg: (
nameValuePair "invoiceplane-cron-${hostName}" (mkIf cfg.cron.enable {
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "5m";
OnUnitActiveSec = "5m";
Unit = "invoiceplane-cron-${hostName}.service";
};
})
)) eachSite;
systemd.services =
mapAttrs' (hostName: cfg: (
nameValuePair "invoiceplane-cron-${hostName}" (mkIf cfg.cron.enable {
serviceConfig = {
Type = "oneshot";
User = user;
ExecStart = "${pkgs.curl}/bin/curl --header 'Host: ${hostName}' http://localhost/invoices/cron/recur/${cfg.cron.key}";
};
})
)) eachSite;
}
(mkIf (cfg.webserver == "caddy") {
services.caddy = {
enable = true;
virtualHosts = mapAttrs' (hostName: cfg: (
nameValuePair "http://${hostName}" {
extraConfig = ''
root * ${pkg hostName cfg}
file_server
php_fastcgi unix/${config.services.phpfpm.pools."invoiceplane-${hostName}".socket}
'';
}
)) eachSite;
};
})
(mkIf (cfg.webserver == "caddy") {
services.caddy = {
enable = true;
virtualHosts =
mapAttrs' (hostName: cfg: (
nameValuePair "http://${hostName}" {
extraConfig = ''
root * ${pkg hostName cfg}
file_server
php_fastcgi unix/${config.services.phpfpm.pools."invoiceplane-${hostName}".socket}
'';
}
))
eachSite;
};
})
]); ]);
} }

View file

@ -5,15 +5,16 @@
flake, flake,
... ...
}: { }: {
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ nixpkgs.config.allowUnfreePredicate = pkg:
"1password" builtins.elem (lib.getName pkg) [
"1password-cli" "1password"
"cups-brother-hl3140cw" "1password-cli"
"facetimehd-firmware" "cups-brother-hl3140cw"
"slack" "facetimehd-firmware"
"veracrypt" "slack"
"zoom" "veracrypt"
]; "zoom"
];
nix = { nix = {
# Use default version alias for nix package # Use default version alias for nix package

View file

@ -16,11 +16,15 @@
services.printing.listenAddresses = ["localhost:631"]; services.printing.listenAddresses = ["localhost:631"];
services.printing.defaultShared = lib.mkDefault false; services.printing.defaultShared = lib.mkDefault false;
services.printing.drivers = [ services.printing.drivers =
pkgs.gutenprint [
] ++ (if (pkgs.system == "x86_64-linux") pkgs.gutenprint
then [ pkgs.cups-brother-hl3140cw ] ]
else []); ++ (
if (pkgs.system == "x86_64-linux")
then [pkgs.cups-brother-hl3140cw]
else []
);
networking.hosts = flake.self.lib.addLocalHostname ["cups.local"]; networking.hosts = flake.self.lib.addLocalHostname ["cups.local"];

View file

@ -1,5 +1,4 @@
{ ... }: {...}: {
{
enable = true; enable = true;
nix-direnv = { nix-direnv = {
enable = true; enable = true;

View file

@ -1,5 +1,4 @@
{ ... }: {...}: {
{
enable = true; enable = true;
extraConfig = { extraConfig = {

View file

@ -44,135 +44,136 @@ in {
universal-ctags universal-ctags
]; ];
plugins = with pkgs.vimPlugins; lib.mkIf cfg.full [ plugins = with pkgs.vimPlugins;
(pkgs.vimPlugins.nvim-treesitter.withPlugins (p: [ lib.mkIf cfg.full [
p.ini (pkgs.vimPlugins.nvim-treesitter.withPlugins (p: [
p.json p.ini
p.json5 p.json
p.markdown p.json5
p.nix p.markdown
p.toml p.nix
p.yaml p.toml
p.yaml
p.css p.css
p.graphql p.graphql
p.html p.html
p.javascript p.javascript
p.scss p.scss
p.tsx p.tsx
p.typescript p.typescript
p.vue p.vue
p.c p.c
p.cpp p.cpp
p.go p.go
p.gomod p.gomod
p.gosum p.gosum
p.haskell p.haskell
p.lua p.lua
p.php p.php
p.python p.python
p.ruby p.ruby
p.rust p.rust
p.vim p.vim
p.vimdoc p.vimdoc
p.passwd p.passwd
p.sql p.sql
p.diff p.diff
p.gitcommit p.gitcommit
p.gitignore p.gitignore
p.git_config p.git_config
p.gitattributes p.gitattributes
p.git_rebase p.git_rebase
p.bash p.bash
p.dockerfile p.dockerfile
p.make p.make
p.ninja p.ninja
p.terraform p.terraform
])) ]))
# Dependencies for nvim-lspconfig # Dependencies for nvim-lspconfig
nvim-cmp nvim-cmp
cmp-nvim-lsp cmp-nvim-lsp
cmp_luasnip cmp_luasnip
luasnip luasnip
# Quickstart configs for neovim LSP # Quickstart configs for neovim LSP
lsp_extensions-nvim lsp_extensions-nvim
nvim-lspconfig nvim-lspconfig
# Collaborative editing in Neovim using built-in capabilities # Collaborative editing in Neovim using built-in capabilities
instant-nvim-nvfetcher instant-nvim-nvfetcher
# Search functionality behind :Ack # Search functionality behind :Ack
ack-vim ack-vim
# The status bar in the bottom of the screen with the mode indication and file location # The status bar in the bottom of the screen with the mode indication and file location
vim-airline vim-airline
# Automatically load editorconfig files in repos to configure nvim settings # Automatically load editorconfig files in repos to configure nvim settings
editorconfig-vim editorconfig-vim
# File browser. Use <leader>n to access # File browser. Use <leader>n to access
nnn-vim nnn-vim
# Highlight characters when using f, F, t, and T # Highlight characters when using f, F, t, and T
quick-scope quick-scope
# Get sudo in vim; :SudaWrite <optional filename> # Get sudo in vim; :SudaWrite <optional filename>
suda-vim suda-vim
# Undo history etc. per project # Undo history etc. per project
vim-workspace-nvfetcher vim-workspace-nvfetcher
# JSON schemas # JSON schemas
SchemaStore-nvim SchemaStore-nvim
# Work with tags files # Work with tags files
vim-gutentags vim-gutentags
# Neovim colorschemes / themes # Neovim colorschemes / themes
sonokai sonokai
vim-hybrid-material vim-hybrid-material
vim-airline-themes vim-airline-themes
vim-apprentice-nvfetcher vim-apprentice-nvfetcher
# Git integrations # Git integrations
# A Git wrapper so awesome, it should be illegal # A Git wrapper so awesome, it should be illegal
fugitive fugitive
# Shows git diff markers in the sign column # Shows git diff markers in the sign column
vim-gitgutter vim-gitgutter
# GitHub extension for fugitive # GitHub extension for fugitive
vim-rhubarb vim-rhubarb
# Ease your git workflow within Vim # Ease your git workflow within Vim
vimagit-nvfetcher vimagit-nvfetcher
# FZF fuzzy finder # FZF fuzzy finder
fzf-vim fzf-vim
fzfWrapper fzfWrapper
# Make the yanked region apparent # Make the yanked region apparent
vim-highlightedyank vim-highlightedyank
# :Beautify Code beautifier # :Beautify Code beautifier
vim-beautify-nvfetcher vim-beautify-nvfetcher
# Unload, delete or wipe a buffer without closing the window # Unload, delete or wipe a buffer without closing the window
vim-bufkill vim-bufkill
# Defaults everyone can agree on # Defaults everyone can agree on
vim-sensible vim-sensible
# emmet for vim: http://emmet.io/ # emmet for vim: http://emmet.io/
emmet-vim emmet-vim
# Caddyfile syntax support for Vim # Caddyfile syntax support for Vim
vim-caddyfile-nvfetcher vim-caddyfile-nvfetcher
# Fix TOFU hashes when writing nix derivations without leaving neovim # Fix TOFU hashes when writing nix derivations without leaving neovim
vim-nixhash vim-nixhash
]; ];
extraConfig = builtins.concatStringsSep "\n" [ extraConfig = builtins.concatStringsSep "\n" [
'' ''

View file

@ -6,96 +6,96 @@
}: let }: let
psCfg = config.pub-solar; psCfg = config.pub-solar;
in in
with lib; { with lib; {
imports = [ imports = [
./home.nix ./home.nix
];
options.pub-solar = {
user = {
name = mkOption {
description = "User login name";
type = types.nullOr types.str;
default = "nixos";
};
description = mkOption {
description = "User description";
type = types.nullOr types.str;
default = "The main PubSolarOS user";
};
password = mkOption {
description = "User password";
type = types.nullOr types.str;
default = null;
};
passwordlessSudo = mkOption {
description = "Whether this user can use sudo without entering a password";
type = types.bool;
default = false;
};
publicKeys = mkOption {
description = "User SSH public keys";
type = types.listOf types.str;
default = [];
};
fullName = mkOption {
description = "User full name";
type = types.nullOr types.str;
default = null;
};
email = mkOption {
description = "User email address";
type = types.nullOr types.str;
default = null;
};
gpgKeyId = mkOption {
description = "GPG Key ID";
type = types.nullOr types.str;
default = null;
};
};
};
config = {
users = {
mutableUsers = false;
users."${psCfg.user.name}" = {
# Indicates whether this is an account for a “real” user.
# This automatically sets group to users, createHome to true,
# home to /home/username, useDefaultShell to true, and isSystemUser to false.
isNormalUser = true;
description = psCfg.user.description;
extraGroups = [
"input"
"lp"
"networkmanager"
"scanner"
"video"
"wheel"
];
shell = pkgs.bash;
initialHashedPassword =
if psCfg.user.password != null
then psCfg.user.password
else "";
openssh.authorizedKeys.keys =
if psCfg.user.publicKeys != null
then psCfg.user.publicKeys
else [];
};
};
security.sudo.extraRules = mkIf psCfg.user.passwordlessSudo [
{
users = ["${psCfg.user.name}"];
commands = [
{
command = "ALL";
options = ["NOPASSWD"];
}
];
}
]; ];
};
} options.pub-solar = {
user = {
name = mkOption {
description = "User login name";
type = types.nullOr types.str;
default = "nixos";
};
description = mkOption {
description = "User description";
type = types.nullOr types.str;
default = "The main PubSolarOS user";
};
password = mkOption {
description = "User password";
type = types.nullOr types.str;
default = null;
};
passwordlessSudo = mkOption {
description = "Whether this user can use sudo without entering a password";
type = types.bool;
default = false;
};
publicKeys = mkOption {
description = "User SSH public keys";
type = types.listOf types.str;
default = [];
};
fullName = mkOption {
description = "User full name";
type = types.nullOr types.str;
default = null;
};
email = mkOption {
description = "User email address";
type = types.nullOr types.str;
default = null;
};
gpgKeyId = mkOption {
description = "GPG Key ID";
type = types.nullOr types.str;
default = null;
};
};
};
config = {
users = {
mutableUsers = false;
users."${psCfg.user.name}" = {
# Indicates whether this is an account for a “real” user.
# This automatically sets group to users, createHome to true,
# home to /home/username, useDefaultShell to true, and isSystemUser to false.
isNormalUser = true;
description = psCfg.user.description;
extraGroups = [
"input"
"lp"
"networkmanager"
"scanner"
"video"
"wheel"
];
shell = pkgs.bash;
initialHashedPassword =
if psCfg.user.password != null
then psCfg.user.password
else "";
openssh.authorizedKeys.keys =
if psCfg.user.publicKeys != null
then psCfg.user.publicKeys
else [];
};
};
security.sudo.extraRules = mkIf psCfg.user.passwordlessSudo [
{
users = ["${psCfg.user.name}"];
commands = [
{
command = "ALL";
options = ["NOPASSWD"];
}
];
}
];
};
}

View file

@ -6,18 +6,16 @@
}: { }: {
flake = { flake = {
nixosModules = rec { nixosModules = rec {
overlays = ({ ... }: { overlays = {...}: {
nixpkgs.overlays = [ nixpkgs.overlays = [
(final: prev: (final: prev: let
let
unstable = import inputs.unstable { unstable = import inputs.unstable {
system = prev.system; system = prev.system;
}; };
master = import inputs.master { master = import inputs.master {
system = prev.system; system = prev.system;
}; };
in in {
{
direnv = unstable.direnv; direnv = unstable.direnv;
nix-direnv = unstable.nix-direnv; nix-direnv = unstable.nix-direnv;
#vimPlugins = prev.vimPlugins // {inherit (unstable.vimPlugins) nvim-lspconfig;}; #vimPlugins = prev.vimPlugins // {inherit (unstable.vimPlugins) nvim-lspconfig;};
@ -31,7 +29,7 @@
(import ./nix-index.nix) (import ./nix-index.nix)
(import ./neovim-plugins.nix) (import ./neovim-plugins.nix)
]; ];
}); };
}; };
}; };
} }

View file

@ -1,5 +1,4 @@
{ self, ... }: {self, ...}: {
{
flake = { flake = {
nixosModules = rec { nixosModules = rec {
root = import ./root; root = import ./root;

View file

@ -1,4 +1,3 @@
{...}: {...}: {
{
users.users.root.hashedPassword = ""; users.users.root.hashedPassword = "";
} }