Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a4af935e6e | |||
| cf308a1371 | |||
| 503c1fe9bc | |||
| 5eec5689f4 |
@@ -35,8 +35,8 @@ In this repo, `flake.nix` imports `./modules` recursively via `inputs.import-tre
|
||||
- `modules/hosts/<name>/default.nix`: host features that assemble NixOS aspects into `flake.modules.nixos.<name>`.
|
||||
- `modules/secrets/`: secret-related features shared by hosts.
|
||||
- `modules/flake-parts.nix`: flake-parts entrypoint; defines systems, formatter, and `flake.nixosConfigurations`.
|
||||
- `modules/lib.nix`: shared constructors in `config.meta.lib`, especially `mkHost`, `mkHostUser`, and `mkCaddyReverseProxy`.
|
||||
- `modules/users.nix`: canonical user attrsets exposed through `meta.lib.users`.
|
||||
- `modules/lib.nix`: shared constructors and helpers in `config.meta.lib`, especially `mkHost` and `mkCaddyReverseProxy`.
|
||||
- `modules/data.nix`: canonical shared repo data and account attrsets exposed through `meta.lib.repo` and `meta.lib.accounts`.
|
||||
- `modules/features/meta.nix`: shared metadata schema for `meta.host` and `meta.user`.
|
||||
|
||||
## How Features Are Applied Here
|
||||
@@ -45,7 +45,7 @@ In this repo, `flake.nix` imports `./modules` recursively via `inputs.import-tre
|
||||
- Reusable Home Manager concerns are published as `flake.modules.homeManager.<name>`.
|
||||
- Hosts are aspects too. `orion`, `polaris`, and `zenith` are `nixos` aspects assembled from smaller aspects.
|
||||
- Host modules should use `config.meta.lib.mkHost` to define `meta.host`, base imports, hostname, and state version.
|
||||
- Per-user Home Manager composition should use `config.meta.lib.mkHostUser` so `meta.host` and `meta.user` stay available to Home Manager features.
|
||||
- Per-host user declarations should stay inline under `users.<name>` using canonical accounts from `meta.lib.accounts`, so host-local defaults stay close to the host and `mkHost` can wire `meta.host` and `meta.user` into Home Manager consistently.
|
||||
- Features may rely on the `meta` contract. Existing modules already read `config.meta.host`, `config.meta.user`, and `config.meta.lib`.
|
||||
|
||||
## Preferred Aspect Patterns
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
userSpecs = {
|
||||
kiri = {
|
||||
realName = "Jelle Spreeuwenberg";
|
||||
homeDirectory = "/home/kiri";
|
||||
emails = {
|
||||
personal = {
|
||||
address = "mail@jelles.net";
|
||||
primary = true;
|
||||
scope = "personal";
|
||||
type = "mxrouting";
|
||||
};
|
||||
old = {
|
||||
address = "mail@jellespreeuwenberg.nl";
|
||||
scope = null;
|
||||
type = "mxrouting";
|
||||
};
|
||||
uni = {
|
||||
address = "j.spreeuwenberg@student.tue.nl";
|
||||
scope = null;
|
||||
type = "office365";
|
||||
};
|
||||
work = {
|
||||
address = "jelle.spreeuwenberg@yookr.org";
|
||||
scope = "work";
|
||||
type = "office365";
|
||||
};
|
||||
};
|
||||
sourceControl = { };
|
||||
};
|
||||
|
||||
ergon = {
|
||||
realName = "Jelle Spreeuwenberg";
|
||||
homeDirectory = "/home/ergon";
|
||||
emails = {
|
||||
personal = {
|
||||
address = "mail@jelles.net";
|
||||
scope = "personal";
|
||||
type = "mxrouting";
|
||||
};
|
||||
work = {
|
||||
address = "jelle.spreeuwenberg@yookr.org";
|
||||
primary = true;
|
||||
scope = "work";
|
||||
type = "office365";
|
||||
};
|
||||
};
|
||||
sourceControl.projectScope = "work";
|
||||
};
|
||||
};
|
||||
|
||||
accounts = lib.mapAttrs (name: spec: spec // { inherit name; }) userSpecs;
|
||||
|
||||
repo = {
|
||||
contact.email = "mail@jelles.net";
|
||||
|
||||
desktop = {
|
||||
browser = {
|
||||
command = "vivaldi";
|
||||
packagePath = [ "vivaldi" ];
|
||||
};
|
||||
|
||||
fileManager = {
|
||||
command = "nautilus";
|
||||
packagePath = [ "nautilus" ];
|
||||
};
|
||||
|
||||
terminal = {
|
||||
command = "kitty";
|
||||
desktopId = "kitty.desktop";
|
||||
packagePath = [ "kitty" ];
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
actual = {
|
||||
domain = "finance.jelles.net";
|
||||
host = "127.0.0.1";
|
||||
port = 3000;
|
||||
url = "https://finance.jelles.net";
|
||||
};
|
||||
|
||||
gitea = {
|
||||
domain = "git.jelles.net";
|
||||
host = "127.0.0.1";
|
||||
port = 3001;
|
||||
url = "https://git.jelles.net/";
|
||||
};
|
||||
|
||||
radicale = {
|
||||
domain = "radicale.jelles.net";
|
||||
host = "127.0.0.1";
|
||||
port = 5232;
|
||||
url = "https://radicale.jelles.net/";
|
||||
};
|
||||
|
||||
vaultwarden = {
|
||||
domain = "vault.jelles.net";
|
||||
host = "127.0.0.1";
|
||||
port = 8100;
|
||||
url = "https://vault.jelles.net";
|
||||
};
|
||||
};
|
||||
|
||||
theme = {
|
||||
cursor = {
|
||||
name = "phinger-cursors-light";
|
||||
packagePath = [ "phinger-cursors" ];
|
||||
size = 24;
|
||||
};
|
||||
|
||||
kanagawa = {
|
||||
displayName = "Kanagawa Wave";
|
||||
name = "kanagawa-wave";
|
||||
gtkThemeName = "Kanagawa-BL-LB";
|
||||
iconThemeName = "Kanagawa";
|
||||
owner = "Fausto-Korpsvart";
|
||||
repo = "Kanagawa-GKT-Theme";
|
||||
rev = "55ca4ba249eba21f861b9866b71ab41bb8930318";
|
||||
hash = "sha256-UdMoMx2DoovcxSp/zBZ3PRv/Qpj+prd0uPm1gmdak2E=";
|
||||
version = "unstable-2025-10-23";
|
||||
|
||||
palette = {
|
||||
background = "#1F1F28";
|
||||
foreground = "#DCD7BA";
|
||||
secondaryBackground = "#16161D";
|
||||
border = "#2A2A37";
|
||||
selectionBackground = "#2D4F67";
|
||||
selectionForeground = "#C8C093";
|
||||
url = "#72A7BC";
|
||||
cursor = "#C8C093";
|
||||
muted = "#727169";
|
||||
|
||||
accents = {
|
||||
blue = "#7E9CD8";
|
||||
green = "#98BB6C";
|
||||
magenta = "#D27E99";
|
||||
orange = "#FFA066";
|
||||
purple = "#957FB8";
|
||||
red = "#E82424";
|
||||
yellow = "#E6C384";
|
||||
cyan = "#7AA89F";
|
||||
};
|
||||
|
||||
niri.border = {
|
||||
active = "#7E9CD8";
|
||||
inactive = "#54546D";
|
||||
urgent = "#E82424";
|
||||
};
|
||||
|
||||
terminal = {
|
||||
color0 = "#16161D";
|
||||
color1 = "#C34043";
|
||||
color2 = "#76946A";
|
||||
color3 = "#C0A36E";
|
||||
color4 = "#7E9CD8";
|
||||
color5 = "#957FB8";
|
||||
color6 = "#6A9589";
|
||||
color7 = "#C8C093";
|
||||
color8 = "#727169";
|
||||
color9 = "#E82424";
|
||||
color10 = "#98BB6C";
|
||||
color11 = "#E6C384";
|
||||
color12 = "#7FB4CA";
|
||||
color13 = "#938AA9";
|
||||
color14 = "#7AA89F";
|
||||
color15 = "#DCD7BA";
|
||||
color16 = "#FFA066";
|
||||
color17 = "#FF5D62";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options.meta.lib.repo = lib.mkOption {
|
||||
type = lib.types.attrs;
|
||||
description = "Internal shared repository metadata.";
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
options.meta.lib.accounts = lib.mkOption {
|
||||
type = lib.types.attrs;
|
||||
description = "Canonical account attrsets shared by host definitions.";
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
config.meta.lib.repo = repo;
|
||||
config.meta.lib.accounts = accounts;
|
||||
}
|
||||
@@ -1,22 +1,26 @@
|
||||
{ ... }:
|
||||
{
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
metaRepo = config.meta.lib.repo;
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager.bitwarden =
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
user = config.meta.user;
|
||||
primaryEmail = builtins.head (lib.filter (email: email.primary) (builtins.attrValues user.emails));
|
||||
in
|
||||
{
|
||||
programs.rbw = {
|
||||
enable = true;
|
||||
settings = {
|
||||
base_url = "https://vault.jelles.net";
|
||||
email = primaryEmail.address;
|
||||
base_url = metaRepo.services.vaultwarden.url;
|
||||
email = user.primaryEmail.address;
|
||||
pinentry = pkgs.pinentry-gnome3;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
homeModules = config.flake.modules.homeManager;
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager.ergon-workstation = {
|
||||
imports = [ homeModules.workstation-base ];
|
||||
};
|
||||
}
|
||||
@@ -3,13 +3,10 @@
|
||||
flake.modules.homeManager.git =
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
user = config.meta.user;
|
||||
primaryEmail = builtins.head (lib.filter (email: email.primary) (builtins.attrValues user.emails));
|
||||
usesScopedIdentity = user != null && user.sourceControl.profiles != { };
|
||||
in
|
||||
{
|
||||
programs.git = {
|
||||
@@ -21,11 +18,9 @@
|
||||
];
|
||||
settings = {
|
||||
init.defaultBranch = "main";
|
||||
}
|
||||
// lib.optionalAttrs (!usesScopedIdentity) {
|
||||
user = {
|
||||
name = user.realName;
|
||||
email = primaryEmail.address;
|
||||
email = user.primaryEmail.address;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
+13
-55
@@ -1,3 +1,10 @@
|
||||
{
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
metaLib = config.meta.lib;
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.input =
|
||||
{
|
||||
@@ -6,64 +13,15 @@
|
||||
...
|
||||
}:
|
||||
let
|
||||
hostInput = config.meta.host.input;
|
||||
|
||||
hasAnyConfiguredValue = attrs: lib.any (value: value != null) (lib.attrValues attrs);
|
||||
libinputScrollMethodMap = {
|
||||
edge = "edge";
|
||||
"no-scroll" = "none";
|
||||
"on-button-down" = "button";
|
||||
"two-finger" = "twofinger";
|
||||
};
|
||||
libinputClickMethodMap = {
|
||||
"button-areas" = "buttonareas";
|
||||
clickfinger = "clickfinger";
|
||||
};
|
||||
|
||||
hasMouseConfig = hasAnyConfiguredValue hostInput.mouse;
|
||||
hasTouchpadConfig = hasAnyConfiguredValue hostInput.touchpad;
|
||||
hasPointerConfig = hasMouseConfig || hasTouchpadConfig;
|
||||
|
||||
mouseConfig = lib.filterAttrs (_: value: value != null) {
|
||||
accelProfile = hostInput.mouse.accelProfile;
|
||||
accelSpeed =
|
||||
if hostInput.mouse.accelSpeed == null then null else toString hostInput.mouse.accelSpeed;
|
||||
leftHanded = hostInput.mouse.leftHanded;
|
||||
middleEmulation = hostInput.mouse.middleEmulation;
|
||||
naturalScrolling = hostInput.mouse.naturalScrolling;
|
||||
scrollMethod =
|
||||
if hostInput.mouse.scrollMethod == null then
|
||||
null
|
||||
else
|
||||
libinputScrollMethodMap.${hostInput.mouse.scrollMethod};
|
||||
};
|
||||
|
||||
touchpadConfig = lib.filterAttrs (_: value: value != null) {
|
||||
accelProfile = hostInput.touchpad.accelProfile;
|
||||
accelSpeed =
|
||||
if hostInput.touchpad.accelSpeed == null then null else toString hostInput.touchpad.accelSpeed;
|
||||
clickMethod =
|
||||
if hostInput.touchpad.clickMethod == null then
|
||||
null
|
||||
else
|
||||
libinputClickMethodMap.${hostInput.touchpad.clickMethod};
|
||||
disableWhileTyping = hostInput.touchpad.disableWhileTyping;
|
||||
leftHanded = hostInput.touchpad.leftHanded;
|
||||
middleEmulation = hostInput.touchpad.middleEmulation;
|
||||
naturalScrolling = hostInput.touchpad.naturalScrolling;
|
||||
scrollMethod =
|
||||
if hostInput.touchpad.scrollMethod == null then
|
||||
null
|
||||
else
|
||||
libinputScrollMethodMap.${hostInput.touchpad.scrollMethod};
|
||||
tapping = hostInput.touchpad.tapping;
|
||||
};
|
||||
inputProfiles = metaLib.mkInputProfiles config.meta.host.input;
|
||||
in
|
||||
{
|
||||
services.libinput = lib.mkIf hasPointerConfig {
|
||||
services.libinput = lib.mkIf inputProfiles.hasPointerConfig {
|
||||
enable = true;
|
||||
mouse = mouseConfig;
|
||||
touchpad = touchpadConfig;
|
||||
inherit (inputProfiles.libinput)
|
||||
mouse
|
||||
touchpad
|
||||
;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
homeModules = config.flake.modules.homeManager;
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager.kiri-workstation = {
|
||||
imports = [
|
||||
homeModules.workstation-base
|
||||
homeModules.syncthing
|
||||
homeModules.qbittorrent-client
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -1,12 +1,23 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
metaRepo = config.meta.lib.repo;
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager.local-apps =
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
browserPackage = config.meta.lib.resolvePackagePath {
|
||||
inherit pkgs;
|
||||
path = metaRepo.desktop.browser.packagePath;
|
||||
};
|
||||
in
|
||||
{
|
||||
home.sessionVariables.BROWSER = "vivaldi";
|
||||
home.sessionVariables.BROWSER = metaRepo.desktop.browser.command;
|
||||
|
||||
home.packages = with pkgs; [
|
||||
home.packages =
|
||||
with pkgs;
|
||||
[
|
||||
brave
|
||||
vivaldi
|
||||
postman
|
||||
spotify
|
||||
calcure
|
||||
@@ -14,7 +25,8 @@
|
||||
unzip
|
||||
gimp
|
||||
dbeaver-bin
|
||||
];
|
||||
]
|
||||
++ [ browserPackage ];
|
||||
|
||||
programs.imv.enable = true;
|
||||
programs.sioyek.enable = true;
|
||||
|
||||
+153
-83
@@ -1,12 +1,73 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
nonEmptyStrType = lib.types.addCheck lib.types.str (value: lib.stringLength value > 0);
|
||||
|
||||
mkNullableOption =
|
||||
type:
|
||||
lib.mkOption {
|
||||
inherit type;
|
||||
type = lib.types.nullOr type;
|
||||
default = null;
|
||||
};
|
||||
|
||||
hasSinglePrimaryEmail =
|
||||
user: builtins.length (lib.filter (email: email.primary) (builtins.attrValues user.emails)) == 1;
|
||||
|
||||
scopeEmailCount =
|
||||
scope: user:
|
||||
builtins.length (lib.filter (email: email.scope == scope) (builtins.attrValues user.emails));
|
||||
|
||||
hasAtMostOneScopedEmail = scope: user: scopeEmailCount scope user <= 1;
|
||||
|
||||
requiredSourceControlScopes =
|
||||
user:
|
||||
lib.unique [
|
||||
"personal"
|
||||
user.sourceControl.projectScope
|
||||
];
|
||||
|
||||
hasRequiredScopedEmail = scope: user: scopeEmailCount scope user == 1;
|
||||
|
||||
scopeEmailAddress =
|
||||
scope: user:
|
||||
let
|
||||
emails = lib.filter (email: email.scope == scope) (builtins.attrValues user.emails);
|
||||
in
|
||||
if emails == [ ] then "" else (builtins.head emails).address;
|
||||
|
||||
primaryEmailFallback = {
|
||||
address = "";
|
||||
primary = false;
|
||||
scope = null;
|
||||
type = "mxrouting";
|
||||
};
|
||||
|
||||
emailProviderType = lib.types.enum [
|
||||
"mxrouting"
|
||||
"office365"
|
||||
];
|
||||
|
||||
sourceControlScopeType = lib.types.enum [
|
||||
"personal"
|
||||
"work"
|
||||
];
|
||||
|
||||
pointerAccelProfileType = lib.types.enum [
|
||||
"adaptive"
|
||||
"flat"
|
||||
];
|
||||
|
||||
pointerScrollMethodType = lib.types.enum [
|
||||
"no-scroll"
|
||||
"two-finger"
|
||||
"edge"
|
||||
"on-button-down"
|
||||
];
|
||||
|
||||
touchpadClickMethodType = lib.types.enum [
|
||||
"button-areas"
|
||||
"clickfinger"
|
||||
];
|
||||
|
||||
emailType = lib.types.submodule (
|
||||
{ ... }:
|
||||
{
|
||||
@@ -17,11 +78,14 @@ let
|
||||
|
||||
primary = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
type = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
type = emailProviderType;
|
||||
};
|
||||
|
||||
scope = mkNullableOption sourceControlScopeType;
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -31,13 +95,11 @@ let
|
||||
{
|
||||
options = {
|
||||
publicKey = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
|
||||
privateKeyPath = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
type = lib.types.nullOr nonEmptyStrType;
|
||||
default = null;
|
||||
};
|
||||
|
||||
privateKeyPath = mkNullableOption nonEmptyStrType;
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -62,28 +124,29 @@ let
|
||||
}
|
||||
);
|
||||
|
||||
sourceControlProfileType = lib.types.submodule (
|
||||
{ ... }:
|
||||
{
|
||||
options = { };
|
||||
}
|
||||
);
|
||||
|
||||
sourceControlType = lib.types.submodule (
|
||||
{ ... }:
|
||||
{
|
||||
options = {
|
||||
profiles = lib.mkOption {
|
||||
type = lib.types.attrsOf sourceControlProfileType;
|
||||
default = { };
|
||||
projectScope = lib.mkOption {
|
||||
type = sourceControlScopeType;
|
||||
default = "personal";
|
||||
};
|
||||
|
||||
projectScope = lib.mkOption {
|
||||
type = lib.types.enum [
|
||||
"personal"
|
||||
"work"
|
||||
];
|
||||
default = "personal";
|
||||
scopes = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule (
|
||||
{ ... }:
|
||||
{
|
||||
options.email = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
readOnly = true;
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
readOnly = true;
|
||||
description = "Derived source-control identities keyed by scope.";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -91,6 +154,10 @@ let
|
||||
|
||||
userType = lib.types.submodule (
|
||||
{ config, ... }:
|
||||
let
|
||||
primaryEmails = lib.filter (email: email.primary) (builtins.attrValues config.emails);
|
||||
primaryEmail = if primaryEmails == [ ] then primaryEmailFallback else builtins.head primaryEmails;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
@@ -105,10 +172,6 @@ let
|
||||
type = lib.types.str;
|
||||
};
|
||||
|
||||
terminalPackagePath = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
};
|
||||
|
||||
nixosConfigurationPath = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "${config.homeDirectory}/.config/nixos";
|
||||
@@ -118,11 +181,24 @@ let
|
||||
type = lib.types.attrsOf emailType;
|
||||
};
|
||||
|
||||
primaryEmail = lib.mkOption {
|
||||
type = emailType;
|
||||
readOnly = true;
|
||||
description = "Derived primary email entry for this user.";
|
||||
};
|
||||
|
||||
sourceControl = lib.mkOption {
|
||||
type = sourceControlType;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
primaryEmail = primaryEmail;
|
||||
sourceControl.scopes = lib.genAttrs (requiredSourceControlScopes config) (scope: {
|
||||
email = scopeEmailAddress scope config;
|
||||
});
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -179,28 +255,12 @@ let
|
||||
{ ... }:
|
||||
{
|
||||
options = {
|
||||
accelProfile = mkNullableOption (
|
||||
lib.types.nullOr (
|
||||
lib.types.enum [
|
||||
"adaptive"
|
||||
"flat"
|
||||
]
|
||||
)
|
||||
);
|
||||
accelSpeed = mkNullableOption (lib.types.nullOr lib.types.float);
|
||||
leftHanded = mkNullableOption (lib.types.nullOr lib.types.bool);
|
||||
middleEmulation = mkNullableOption (lib.types.nullOr lib.types.bool);
|
||||
naturalScrolling = mkNullableOption (lib.types.nullOr lib.types.bool);
|
||||
scrollMethod = mkNullableOption (
|
||||
lib.types.nullOr (
|
||||
lib.types.enum [
|
||||
"no-scroll"
|
||||
"two-finger"
|
||||
"edge"
|
||||
"on-button-down"
|
||||
]
|
||||
)
|
||||
);
|
||||
accelProfile = mkNullableOption (pointerAccelProfileType);
|
||||
accelSpeed = mkNullableOption lib.types.float;
|
||||
leftHanded = mkNullableOption lib.types.bool;
|
||||
middleEmulation = mkNullableOption lib.types.bool;
|
||||
naturalScrolling = mkNullableOption lib.types.bool;
|
||||
scrollMethod = mkNullableOption pointerScrollMethodType;
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -209,38 +269,15 @@ let
|
||||
{ ... }:
|
||||
{
|
||||
options = {
|
||||
accelProfile = mkNullableOption (
|
||||
lib.types.nullOr (
|
||||
lib.types.enum [
|
||||
"adaptive"
|
||||
"flat"
|
||||
]
|
||||
)
|
||||
);
|
||||
accelSpeed = mkNullableOption (lib.types.nullOr lib.types.float);
|
||||
clickMethod = mkNullableOption (
|
||||
lib.types.nullOr (
|
||||
lib.types.enum [
|
||||
"button-areas"
|
||||
"clickfinger"
|
||||
]
|
||||
)
|
||||
);
|
||||
disableWhileTyping = mkNullableOption (lib.types.nullOr lib.types.bool);
|
||||
leftHanded = mkNullableOption (lib.types.nullOr lib.types.bool);
|
||||
middleEmulation = mkNullableOption (lib.types.nullOr lib.types.bool);
|
||||
naturalScrolling = mkNullableOption (lib.types.nullOr lib.types.bool);
|
||||
scrollMethod = mkNullableOption (
|
||||
lib.types.nullOr (
|
||||
lib.types.enum [
|
||||
"no-scroll"
|
||||
"two-finger"
|
||||
"edge"
|
||||
"on-button-down"
|
||||
]
|
||||
)
|
||||
);
|
||||
tapping = mkNullableOption (lib.types.nullOr lib.types.bool);
|
||||
accelProfile = mkNullableOption (pointerAccelProfileType);
|
||||
accelSpeed = mkNullableOption lib.types.float;
|
||||
clickMethod = mkNullableOption touchpadClickMethodType;
|
||||
disableWhileTyping = mkNullableOption lib.types.bool;
|
||||
leftHanded = mkNullableOption lib.types.bool;
|
||||
middleEmulation = mkNullableOption lib.types.bool;
|
||||
naturalScrolling = mkNullableOption lib.types.bool;
|
||||
scrollMethod = mkNullableOption pointerScrollMethodType;
|
||||
tapping = mkNullableOption lib.types.bool;
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -292,15 +329,44 @@ let
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
mkUserEmailAssertions =
|
||||
userName: user:
|
||||
[
|
||||
{
|
||||
assertion = hasSinglePrimaryEmail user;
|
||||
message = "User `${userName}` must define exactly one primary email entry.";
|
||||
}
|
||||
]
|
||||
++ (map
|
||||
(scope: {
|
||||
assertion = hasAtMostOneScopedEmail scope user;
|
||||
message = "User `${userName}` may define at most one `${scope}` scoped email entry.";
|
||||
})
|
||||
[
|
||||
"personal"
|
||||
"work"
|
||||
]
|
||||
)
|
||||
++ map (scope: {
|
||||
assertion = hasRequiredScopedEmail scope user;
|
||||
message = "User `${userName}` must define exactly one `${scope}` scoped email entry.";
|
||||
}) (requiredSourceControlScopes user);
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.meta = {
|
||||
flake.modules.nixos.meta =
|
||||
{ config, ... }:
|
||||
{
|
||||
options.meta.host = lib.mkOption {
|
||||
type = hostType;
|
||||
};
|
||||
|
||||
config.assertions = lib.flatten (lib.mapAttrsToList mkUserEmailAssertions config.meta.host.users);
|
||||
};
|
||||
|
||||
flake.modules.homeManager.meta = {
|
||||
flake.modules.homeManager.meta =
|
||||
{ config, ... }:
|
||||
{
|
||||
options.meta = {
|
||||
host = lib.mkOption {
|
||||
type = lib.types.nullOr hostType;
|
||||
@@ -312,5 +378,9 @@ in
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
|
||||
config.assertions = lib.optionals (config.meta.user != null) (
|
||||
mkUserEmailAssertions config.meta.user.name config.meta.user
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{ themeName }:
|
||||
''
|
||||
require("kanagawa").setup({
|
||||
colors = {
|
||||
theme = {
|
||||
all = {
|
||||
ui = {
|
||||
bg_gutter = "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
overrides = function(colors)
|
||||
local theme = colors.theme
|
||||
|
||||
local makeDiagnosticColor = function(color)
|
||||
local c = require("kanagawa.lib.color")
|
||||
return { fg = color, bg = c(color):blend(theme.ui.bg, 0.95):to_hex() }
|
||||
end
|
||||
|
||||
return {
|
||||
TelescopeTitle = { fg = theme.ui.special, bold = true },
|
||||
TelescopePromptNormal = { bg = theme.ui.bg_p1 },
|
||||
TelescopePromptBorder = { fg = theme.ui.bg_p1, bg = theme.ui.bg_p1 },
|
||||
TelescopeResultsNormal = { fg = theme.ui.fg_dim, bg = theme.ui.bg_m1 },
|
||||
TelescopeResultsBorder = { fg = theme.ui.bg_m1, bg = theme.ui.bg_m1 },
|
||||
TelescopePreviewNormal = { bg = theme.ui.bg_dim },
|
||||
TelescopePreviewBorder = { bg = theme.ui.bg_dim, fg = theme.ui.bg_dim },
|
||||
|
||||
Pmenu = { fg = theme.ui.shade0, bg = theme.ui.bg_p1 }, -- add `blend = vim.o.pumblend` to enable transparency
|
||||
PmenuSel = { fg = "NONE", bg = theme.ui.bg_p2 },
|
||||
PmenuSbar = { bg = theme.ui.bg_m1 },
|
||||
PmenuThumb = { bg = theme.ui.bg_p2 },
|
||||
|
||||
DiagnosticVirtualTextHint = makeDiagnosticColor(theme.diag.hint),
|
||||
DiagnosticVirtualTextInfo = makeDiagnosticColor(theme.diag.info),
|
||||
DiagnosticVirtualTextWarn = makeDiagnosticColor(theme.diag.warning),
|
||||
DiagnosticVirtualTextError = makeDiagnosticColor(theme.diag.error),
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
vim.cmd.colorscheme("${themeName}")
|
||||
''
|
||||
@@ -1,3 +1,7 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
repoTheme = config.meta.lib.repo.theme.kanagawa;
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager.neovim =
|
||||
{
|
||||
@@ -109,56 +113,13 @@
|
||||
# Hostname/ConfigDir needed for nixd
|
||||
nixdExtras = {
|
||||
nixpkgs = "import ${pkgs.path} {}";
|
||||
nixos_options = ''(builtins.getFlake "path://${config.home.homeDirectory}/.config/nixos").nixosConfigurations.${config.meta.host.name}.options'';
|
||||
home_manager_options = ''(builtins.getFlake "path://${config.home.homeDirectory}/.config/nixos").nixosConfigurations.${config.meta.host.name}.options.home-manager.users.type.getSubOptions []'';
|
||||
nixos_options = ''(builtins.getFlake "path://${config.meta.user.nixosConfigurationPath}").nixosConfigurations.${config.meta.host.name}.options'';
|
||||
home_manager_options = ''(builtins.getFlake "path://${config.meta.user.nixosConfigurationPath}").nixosConfigurations.${config.meta.host.name}.options.home-manager.users.type.getSubOptions []'';
|
||||
};
|
||||
|
||||
# TODO: Put in separate theme file
|
||||
themeSetup = # lua
|
||||
''
|
||||
require("kanagawa").setup({
|
||||
colors = {
|
||||
theme = {
|
||||
all = {
|
||||
ui = {
|
||||
bg_gutter = "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
overrides = function(colors)
|
||||
local theme = colors.theme
|
||||
|
||||
local makeDiagnosticColor = function(color)
|
||||
local c = require("kanagawa.lib.color")
|
||||
return { fg = color, bg = c(color):blend(theme.ui.bg, 0.95):to_hex() }
|
||||
end
|
||||
|
||||
return {
|
||||
TelescopeTitle = { fg = theme.ui.special, bold = true },
|
||||
TelescopePromptNormal = { bg = theme.ui.bg_p1 },
|
||||
TelescopePromptBorder = { fg = theme.ui.bg_p1, bg = theme.ui.bg_p1 },
|
||||
TelescopeResultsNormal = { fg = theme.ui.fg_dim, bg = theme.ui.bg_m1 },
|
||||
TelescopeResultsBorder = { fg = theme.ui.bg_m1, bg = theme.ui.bg_m1 },
|
||||
TelescopePreviewNormal = { bg = theme.ui.bg_dim },
|
||||
TelescopePreviewBorder = { bg = theme.ui.bg_dim, fg = theme.ui.bg_dim },
|
||||
|
||||
Pmenu = { fg = theme.ui.shade0, bg = theme.ui.bg_p1 }, -- add `blend = vim.o.pumblend` to enable transparency
|
||||
PmenuSel = { fg = "NONE", bg = theme.ui.bg_p2 },
|
||||
PmenuSbar = { bg = theme.ui.bg_m1 },
|
||||
PmenuThumb = { bg = theme.ui.bg_p2 },
|
||||
|
||||
DiagnosticVirtualTextHint = makeDiagnosticColor(theme.diag.hint),
|
||||
DiagnosticVirtualTextInfo = makeDiagnosticColor(theme.diag.info),
|
||||
DiagnosticVirtualTextWarn = makeDiagnosticColor(theme.diag.warning),
|
||||
DiagnosticVirtualTextError = makeDiagnosticColor(theme.diag.error),
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
vim.cmd.colorscheme("kanagawa-wave")
|
||||
'';
|
||||
themeSetup = import ./_kanagawa-theme.nix {
|
||||
themeName = repoTheme.name;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
networking = {
|
||||
firewall.enable = true;
|
||||
firewall.allowPing = false;
|
||||
nftables.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
browserCommand,
|
||||
lib,
|
||||
terminalPackage,
|
||||
}:
|
||||
@@ -8,7 +9,7 @@
|
||||
hotkey-overlay.title = "Terminal";
|
||||
};
|
||||
"Mod+B" = {
|
||||
action.spawn = "vivaldi";
|
||||
action.spawn = browserCommand;
|
||||
hotkey-overlay.title = "Browser";
|
||||
};
|
||||
"Mod+Space" = {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
}:
|
||||
let
|
||||
metaLib = config.meta.lib;
|
||||
metaRepo = metaLib.repo;
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.niri =
|
||||
@@ -30,8 +31,12 @@ in
|
||||
...
|
||||
}:
|
||||
let
|
||||
hostInput = config.meta.host.input;
|
||||
|
||||
repoTheme = metaRepo.theme.kanagawa;
|
||||
browserCommand = metaRepo.desktop.browser.command;
|
||||
fileManagerPackage = metaLib.resolvePackagePath {
|
||||
inherit pkgs;
|
||||
path = metaRepo.desktop.fileManager.packagePath;
|
||||
};
|
||||
outputs = lib.mapAttrs (
|
||||
_: display:
|
||||
{
|
||||
@@ -43,51 +48,22 @@ in
|
||||
// lib.optionalAttrs (display.primary or false) {
|
||||
"focus-at-startup" = true;
|
||||
}
|
||||
// lib.optionalAttrs (display ? scale) {
|
||||
// lib.optionalAttrs (display.scale != null) {
|
||||
inherit (display) scale;
|
||||
}
|
||||
// lib.optionalAttrs (display ? mode) {
|
||||
// lib.optionalAttrs (display.mode != null) {
|
||||
inherit (display) mode;
|
||||
}
|
||||
) config.meta.host.displays;
|
||||
terminalPackage = metaLib.resolvePackagePath {
|
||||
inputProfiles = metaLib.mkInputProfiles config.meta.host.input;
|
||||
terminal = metaLib.resolveRepoTerminal {
|
||||
inherit pkgs;
|
||||
path = config.meta.user.terminalPackagePath;
|
||||
};
|
||||
hasMainProgram = terminalPackage != null && terminalPackage ? meta.mainProgram;
|
||||
|
||||
mouseSettings = lib.filterAttrs (_: value: value != null) {
|
||||
accel-profile = hostInput.mouse.accelProfile;
|
||||
accel-speed = hostInput.mouse.accelSpeed;
|
||||
left-handed = hostInput.mouse.leftHanded;
|
||||
middle-emulation = hostInput.mouse.middleEmulation;
|
||||
natural-scroll = hostInput.mouse.naturalScrolling;
|
||||
scroll-method = hostInput.mouse.scrollMethod;
|
||||
};
|
||||
|
||||
touchpadSettings = lib.filterAttrs (_: value: value != null) {
|
||||
accel-profile = hostInput.touchpad.accelProfile;
|
||||
accel-speed = hostInput.touchpad.accelSpeed;
|
||||
click-method = hostInput.touchpad.clickMethod;
|
||||
dwt = hostInput.touchpad.disableWhileTyping;
|
||||
left-handed = hostInput.touchpad.leftHanded;
|
||||
middle-emulation = hostInput.touchpad.middleEmulation;
|
||||
natural-scroll = hostInput.touchpad.naturalScrolling;
|
||||
scroll-method = hostInput.touchpad.scrollMethod;
|
||||
tap = hostInput.touchpad.tapping;
|
||||
};
|
||||
in
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = terminalPackage != null;
|
||||
message = "Unknown terminal package `${lib.showAttrPath config.meta.user.terminalPackagePath}` for user `${config.meta.user.name}`.";
|
||||
}
|
||||
{
|
||||
assertion = hasMainProgram;
|
||||
message = "Terminal package `${lib.showAttrPath config.meta.user.terminalPackagePath}` must define `meta.mainProgram`.";
|
||||
}
|
||||
];
|
||||
assertions = metaLib.mkTerminalAssertions {
|
||||
inherit terminal;
|
||||
};
|
||||
|
||||
home.sessionVariables.NIXOS_OZONE_WL = "1";
|
||||
|
||||
@@ -97,12 +73,14 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
home.packages = with pkgs; [
|
||||
home.packages =
|
||||
with pkgs;
|
||||
[
|
||||
playerctl
|
||||
nautilus
|
||||
brightnessctl
|
||||
xwayland-satellite
|
||||
];
|
||||
]
|
||||
++ [ fileManagerPackage ];
|
||||
|
||||
programs.niri.settings = {
|
||||
inherit outputs;
|
||||
@@ -110,7 +88,6 @@ in
|
||||
spawn-at-startup = [
|
||||
{ command = [ "xwayland-satellite" ]; }
|
||||
{ command = [ "noctalia-shell" ]; }
|
||||
{ command = [ "qbittorrent" ]; }
|
||||
];
|
||||
prefer-no-csd = true;
|
||||
hotkey-overlay.skip-at-startup = true;
|
||||
@@ -135,9 +112,9 @@ in
|
||||
border = {
|
||||
enable = true;
|
||||
width = 3;
|
||||
active.color = "#7E9CD8";
|
||||
inactive.color = "#54546D";
|
||||
urgent.color = "#E82424";
|
||||
active.color = repoTheme.palette.niri.border.active;
|
||||
inactive.color = repoTheme.palette.niri.border.inactive;
|
||||
urgent.color = repoTheme.palette.niri.border.urgent;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -167,20 +144,21 @@ in
|
||||
xkb.options = "caps:escape";
|
||||
};
|
||||
}
|
||||
// lib.optionalAttrs (mouseSettings != { }) {
|
||||
mouse = mouseSettings;
|
||||
// lib.optionalAttrs (inputProfiles.niri.mouse != { }) {
|
||||
mouse = inputProfiles.niri.mouse;
|
||||
}
|
||||
// lib.optionalAttrs (touchpadSettings != { }) {
|
||||
touchpad = touchpadSettings;
|
||||
// lib.optionalAttrs (inputProfiles.niri.touchpad != { }) {
|
||||
touchpad = inputProfiles.niri.touchpad;
|
||||
};
|
||||
|
||||
binds =
|
||||
if hasMainProgram then
|
||||
if terminal.hasPackage then
|
||||
import ./_bindings.nix {
|
||||
inherit
|
||||
browserCommand
|
||||
lib
|
||||
terminalPackage
|
||||
;
|
||||
terminalPackage = terminal.package;
|
||||
}
|
||||
else
|
||||
{ };
|
||||
|
||||
@@ -6,6 +6,17 @@
|
||||
let
|
||||
homeModules = config.flake.modules.homeManager;
|
||||
metaLib = config.meta.lib;
|
||||
mkNoctaliaSettings =
|
||||
{
|
||||
lib,
|
||||
terminalPackage,
|
||||
}:
|
||||
import ./_noctalia-config.nix {
|
||||
inherit
|
||||
lib
|
||||
terminalPackage
|
||||
;
|
||||
};
|
||||
mkPortableSettings =
|
||||
baseSettings:
|
||||
lib.recursiveUpdate baseSettings {
|
||||
@@ -29,43 +40,54 @@ let
|
||||
};
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager.noctalia =
|
||||
flake.modules.homeManager.noctalia-base =
|
||||
{
|
||||
inputs,
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
terminalPackage = metaLib.resolvePackagePath {
|
||||
terminal = metaLib.resolveRepoTerminal {
|
||||
inherit pkgs;
|
||||
path = config.meta.user.terminalPackagePath;
|
||||
};
|
||||
hasMainProgram = terminalPackage != null && terminalPackage ? meta.mainProgram;
|
||||
baseSettings =
|
||||
if hasMainProgram then
|
||||
import ./_noctalia-config.nix {
|
||||
inherit
|
||||
lib
|
||||
terminalPackage
|
||||
;
|
||||
if terminal.hasPackage then
|
||||
mkNoctaliaSettings {
|
||||
inherit lib;
|
||||
terminalPackage = terminal.package;
|
||||
}
|
||||
else
|
||||
{ };
|
||||
in
|
||||
{
|
||||
imports = [ inputs.noctalia.homeModules.default ];
|
||||
options.meta.lib.noctaliaBaseSettings = lib.mkOption {
|
||||
type = lib.types.attrs;
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
assertions = [
|
||||
config = {
|
||||
meta.lib.noctaliaBaseSettings = baseSettings;
|
||||
|
||||
assertions = metaLib.mkTerminalAssertions {
|
||||
inherit terminal;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
flake.modules.homeManager.noctalia =
|
||||
{
|
||||
assertion = terminalPackage != null;
|
||||
message = "Unknown terminal package `${lib.showAttrPath config.meta.user.terminalPackagePath}` for user `${config.meta.user.name}`.";
|
||||
}
|
||||
config,
|
||||
inputs,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
assertion = hasMainProgram;
|
||||
message = "Terminal package `${lib.showAttrPath config.meta.user.terminalPackagePath}` must define `meta.mainProgram`.";
|
||||
}
|
||||
imports = [
|
||||
homeModules.noctalia-base
|
||||
inputs.noctalia.homeModules.default
|
||||
];
|
||||
|
||||
programs.noctalia-shell = {
|
||||
@@ -76,7 +98,7 @@ in
|
||||
}
|
||||
);
|
||||
|
||||
settings = baseSettings;
|
||||
settings = config.meta.lib.noctaliaBaseSettings;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -84,30 +106,15 @@ in
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
terminalPackage = metaLib.resolvePackagePath {
|
||||
inherit pkgs;
|
||||
path = config.meta.user.terminalPackagePath;
|
||||
};
|
||||
hasMainProgram = terminalPackage != null && terminalPackage ? meta.mainProgram;
|
||||
baseSettings =
|
||||
if hasMainProgram then
|
||||
import ./_noctalia-config.nix {
|
||||
inherit
|
||||
lib
|
||||
terminalPackage
|
||||
;
|
||||
}
|
||||
else
|
||||
{ };
|
||||
in
|
||||
{
|
||||
imports = [ homeModules.noctalia ];
|
||||
programs.noctalia-shell.settings = lib.mkForce (
|
||||
if hasMainProgram then mkPortableSettings baseSettings else { }
|
||||
if config.meta.lib.noctaliaBaseSettings == { } then
|
||||
{ }
|
||||
else
|
||||
mkPortableSettings config.meta.lib.noctaliaBaseSettings
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
homeModules = config.flake.modules.homeManager;
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager.personal-productivity = {
|
||||
imports = [
|
||||
homeModules.bitwarden
|
||||
homeModules.email
|
||||
homeModules.pim
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
{
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
metaRepo = config.meta.lib.repo;
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager.pim =
|
||||
{
|
||||
@@ -44,7 +51,7 @@
|
||||
};
|
||||
|
||||
remote = {
|
||||
url = "https://radicale.jelles.net/";
|
||||
url = metaRepo.services.radicale.url;
|
||||
type = "caldav";
|
||||
userName = config.home.username;
|
||||
passwordCommand = [
|
||||
|
||||
@@ -7,8 +7,16 @@
|
||||
};
|
||||
|
||||
flake.modules.homeManager.qbittorrent-client =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
home.packages = [ pkgs.qbittorrent ];
|
||||
|
||||
programs.niri.settings.spawn-at-startup = lib.mkAfter [
|
||||
{ command = [ "qbittorrent" ]; }
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
metaRepo = config.meta.lib.repo;
|
||||
metaLib = config.meta.lib;
|
||||
service = metaRepo.services.actual;
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.actual =
|
||||
@@ -11,15 +13,17 @@ in
|
||||
enable = true;
|
||||
openFirewall = false;
|
||||
settings = {
|
||||
port = 3000;
|
||||
hostname = "127.0.0.1";
|
||||
inherit (service) port;
|
||||
hostname = service.host;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
(metaLib.mkCaddyReverseProxy {
|
||||
domain = "finance.jelles.net";
|
||||
port = 3000;
|
||||
inherit (service)
|
||||
domain
|
||||
port
|
||||
;
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
metaRepo = config.meta.lib.repo;
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.caddy = {
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
email = "mail@jelles.net";
|
||||
email = metaRepo.contact.email;
|
||||
openFirewall = true;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
flake.modules.nixos.deluge-service =
|
||||
{ ... }:
|
||||
{
|
||||
sops.secrets.deluge-auth-file = { };
|
||||
|
||||
services.deluge = {
|
||||
enable = true;
|
||||
declarative = false;
|
||||
};
|
||||
};
|
||||
|
||||
flake.modules.homeManager.deluge-client =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
home.packages = [ pkgs.deluge ];
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
metaRepo = config.meta.lib.repo;
|
||||
metaLib = config.meta.lib;
|
||||
service = metaRepo.services.gitea;
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.gitea =
|
||||
@@ -12,10 +14,10 @@ in
|
||||
|
||||
settings = {
|
||||
server = {
|
||||
DOMAIN = "git.jelles.net";
|
||||
ROOT_URL = "https://git.jelles.net/";
|
||||
HTTP_PORT = 3001;
|
||||
HTTP_ADDR = "127.0.0.1";
|
||||
DOMAIN = service.domain;
|
||||
ROOT_URL = service.url;
|
||||
HTTP_PORT = service.port;
|
||||
HTTP_ADDR = service.host;
|
||||
|
||||
START_SSH_SERVER = false;
|
||||
SSH_PORT = 22;
|
||||
@@ -31,8 +33,10 @@ in
|
||||
}
|
||||
|
||||
(metaLib.mkCaddyReverseProxy {
|
||||
domain = "git.jelles.net";
|
||||
port = 3001;
|
||||
inherit (service)
|
||||
domain
|
||||
port
|
||||
;
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{ ... }:
|
||||
{
|
||||
flake.modules.nixos.qbittorrent = {
|
||||
services.qbittorrent = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
torrentingPort = 43864;
|
||||
webuiPort = 8123;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
metaRepo = config.meta.lib.repo;
|
||||
metaLib = config.meta.lib;
|
||||
service = metaRepo.services.radicale;
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.radicale =
|
||||
@@ -10,7 +12,7 @@ in
|
||||
services.radicale = {
|
||||
enable = true;
|
||||
settings = {
|
||||
server.hosts = [ "127.0.0.1:5232" ];
|
||||
server.hosts = [ "${service.host}:${toString service.port}" ];
|
||||
|
||||
auth = {
|
||||
type = "htpasswd";
|
||||
@@ -24,8 +26,10 @@ in
|
||||
}
|
||||
|
||||
(metaLib.mkCaddyReverseProxy {
|
||||
domain = "radicale.jelles.net";
|
||||
port = 5232;
|
||||
inherit (service)
|
||||
domain
|
||||
port
|
||||
;
|
||||
extraHeaders = [
|
||||
{
|
||||
name = "X-Script-Name";
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
metaRepo = config.meta.lib.repo;
|
||||
metaLib = config.meta.lib;
|
||||
service = metaRepo.services.vaultwarden;
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.vaultwarden =
|
||||
@@ -11,17 +13,19 @@ in
|
||||
enable = true;
|
||||
backupDir = "/var/backup/vaultwarden";
|
||||
config = {
|
||||
DOMAIN = "https://vault.jelles.net";
|
||||
DOMAIN = service.url;
|
||||
SIGNUPS_ALLOWED = false;
|
||||
ROCKET_PORT = 8100;
|
||||
ROCKET_PORT = service.port;
|
||||
ROCKET_LOG = "critical";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
(metaLib.mkCaddyReverseProxy {
|
||||
domain = "vault.jelles.net";
|
||||
port = 8100;
|
||||
inherit (service)
|
||||
domain
|
||||
port
|
||||
;
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
@@ -13,51 +13,26 @@ in
|
||||
host = config.meta.host;
|
||||
user = config.meta.user;
|
||||
sourceControl = user.sourceControl;
|
||||
sourceControlScopes = sourceControl.scopes;
|
||||
hostSourceControlUsers = host.sourceControl.users;
|
||||
hostUserSourceControl =
|
||||
if lib.hasAttr user.name hostSourceControlUsers then hostSourceControlUsers.${user.name} else { };
|
||||
profileNames = builtins.attrNames sourceControl.profiles;
|
||||
hostUserSourceControl = hostSourceControlUsers.${user.name} or { };
|
||||
|
||||
parsedProfiles = map (
|
||||
name:
|
||||
let
|
||||
matches = builtins.match "(github|gitlab)-(personal|work)" name;
|
||||
in
|
||||
{
|
||||
inherit matches name;
|
||||
isValid = matches != null;
|
||||
scope = if matches == null then null else builtins.elemAt matches 1;
|
||||
}
|
||||
) profileNames;
|
||||
|
||||
validProfiles = builtins.filter (profile: profile.isValid) parsedProfiles;
|
||||
invalidProfileNames = map (profile: profile.name) (
|
||||
builtins.filter (profile: !profile.isValid) parsedProfiles
|
||||
);
|
||||
|
||||
emailNamesForScope = {
|
||||
personal = [
|
||||
"personal"
|
||||
"main"
|
||||
];
|
||||
work = [ "work" ];
|
||||
};
|
||||
|
||||
scopeEmails =
|
||||
scope:
|
||||
map (name: user.emails.${name}) (
|
||||
builtins.filter (name: lib.hasAttr name user.emails) emailNamesForScope.${scope}
|
||||
);
|
||||
scopeConfig = scope: hostUserSourceControl.${scope} or null;
|
||||
scopeIdentity = scope: sourceControlScopes.${scope} or null;
|
||||
|
||||
emailForScope =
|
||||
scope:
|
||||
let
|
||||
emails = scopeEmails scope;
|
||||
identity = scopeIdentity scope;
|
||||
in
|
||||
if builtins.length emails == 1 then (builtins.head emails).address else null;
|
||||
if identity == null then null else identity.email;
|
||||
|
||||
scopeConfig =
|
||||
scope: if lib.hasAttr scope hostUserSourceControl then hostUserSourceControl.${scope} else null;
|
||||
scopeHasSigningKey =
|
||||
scope:
|
||||
let
|
||||
keyConfig = scopeConfig scope;
|
||||
in
|
||||
keyConfig != null && keyConfig.publicKey != null;
|
||||
|
||||
privateKeyPathForScope =
|
||||
scope:
|
||||
@@ -69,35 +44,34 @@ in
|
||||
else
|
||||
keyConfig.privateKeyPath;
|
||||
|
||||
scopePublicKey =
|
||||
publicKeyForScope =
|
||||
scope:
|
||||
let
|
||||
keyConfig = scopeConfig scope;
|
||||
in
|
||||
if keyConfig == null then null else keyConfig.publicKey;
|
||||
|
||||
scopesInUse = lib.unique (
|
||||
[
|
||||
"personal"
|
||||
sourceControl.projectScope
|
||||
]
|
||||
++ map (profile: profile.scope) validProfiles
|
||||
scopesInUse = builtins.attrNames sourceControlScopes;
|
||||
|
||||
allowedSignersLines = map (scope: "${emailForScope scope} ${publicKeyForScope scope}") (
|
||||
builtins.filter (scope: emailForScope scope != null && scopeHasSigningKey scope) scopesInUse
|
||||
);
|
||||
|
||||
missingKeyScopes = builtins.filter (scope: scopePublicKey scope == null) scopesInUse;
|
||||
invalidEmailScopes = builtins.filter (scope: emailForScope scope == null) scopesInUse;
|
||||
allowedSignersLines = map (scope: "${emailForScope scope} ${scopePublicKey scope}") (
|
||||
builtins.filter (scope: emailForScope scope != null && scopePublicKey scope != null) scopesInUse
|
||||
);
|
||||
|
||||
gitConfigForScope = scope: {
|
||||
gpg.ssh.allowedSignersFile = "${config.xdg.configHome}/git/allowed_signers";
|
||||
gitConfigForScope =
|
||||
scope:
|
||||
lib.recursiveUpdate
|
||||
{
|
||||
user = {
|
||||
name = user.realName;
|
||||
email = emailForScope scope;
|
||||
signingKey = "${privateKeyPathForScope scope}.pub";
|
||||
};
|
||||
};
|
||||
}
|
||||
(
|
||||
lib.optionalAttrs (scopeHasSigningKey scope) {
|
||||
gpg.ssh.allowedSignersFile = "${config.xdg.configHome}/git/allowed_signers";
|
||||
user.signingKey = "${privateKeyPathForScope scope}.pub";
|
||||
}
|
||||
);
|
||||
|
||||
gitRoots = [
|
||||
{
|
||||
@@ -113,21 +87,6 @@ in
|
||||
{
|
||||
imports = [ homeModules.git ];
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion = invalidProfileNames == [ ];
|
||||
message = "Invalid source control profiles for `${user.name}`: ${lib.concatStringsSep ", " invalidProfileNames}. Expected `<service>-<scope>` using github/gitlab and personal/work.";
|
||||
}
|
||||
{
|
||||
assertion = missingKeyScopes == [ ];
|
||||
message = "Missing source control keys for `${user.name}` scopes: ${lib.concatStringsSep ", " missingKeyScopes}.";
|
||||
}
|
||||
{
|
||||
assertion = invalidEmailScopes == [ ];
|
||||
message = "Expected exactly one email selected by name for `${user.name}` scopes: ${lib.concatStringsSep ", " invalidEmailScopes}. Personal uses `personal` or `main`; work uses `work`.";
|
||||
}
|
||||
];
|
||||
|
||||
xdg.configFile."git/allowed_signers".text = lib.concatStringsSep "\n" (
|
||||
allowedSignersLines ++ [ "" ]
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
metaLib = config.meta.lib;
|
||||
metaRepo = metaLib.repo;
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager.terminal =
|
||||
@@ -11,38 +12,21 @@ in
|
||||
...
|
||||
}:
|
||||
let
|
||||
terminalPackage = metaLib.resolvePackagePath {
|
||||
repoTheme = metaRepo.theme.kanagawa;
|
||||
palette = repoTheme.palette;
|
||||
terminal = metaLib.resolveRepoTerminal {
|
||||
inherit pkgs;
|
||||
path = config.meta.user.terminalPackagePath;
|
||||
};
|
||||
hasTerminalPackage = terminalPackage != null;
|
||||
hasMainProgram = hasTerminalPackage && terminalPackage ? meta.mainProgram;
|
||||
terminalDesktopId = if hasMainProgram then "${terminalPackage.meta.mainProgram}.desktop" else null;
|
||||
in
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = hasTerminalPackage;
|
||||
message = "Unknown terminal package `${lib.showAttrPath config.meta.user.terminalPackagePath}` for user `${config.meta.user.name}`.";
|
||||
}
|
||||
{
|
||||
assertion = hasMainProgram;
|
||||
message = "Terminal package `${lib.showAttrPath config.meta.user.terminalPackagePath}` must define `meta.mainProgram`.";
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
hasMainProgram && builtins.pathExists "${terminalPackage}/share/applications/${terminalDesktopId}";
|
||||
message = "Terminal package `${lib.showAttrPath config.meta.user.terminalPackagePath}` must provide `${terminalDesktopId}`.";
|
||||
}
|
||||
{
|
||||
assertion = hasMainProgram && terminalPackage.meta.mainProgram == "kitty";
|
||||
message = "The terminal feature currently only supports kitty-specific Home Manager configuration.";
|
||||
}
|
||||
];
|
||||
assertions = metaLib.mkTerminalAssertions {
|
||||
inherit terminal;
|
||||
requireDesktopEntry = true;
|
||||
};
|
||||
|
||||
xdg.terminal-exec = {
|
||||
enable = true;
|
||||
settings.default = lib.optional (terminalDesktopId != null) terminalDesktopId;
|
||||
settings.default = lib.optional (terminal.desktopId != null) terminal.desktopId;
|
||||
};
|
||||
|
||||
programs.kitty = {
|
||||
@@ -60,43 +44,43 @@ in
|
||||
update_check_interval = 0;
|
||||
};
|
||||
extraConfig = ''
|
||||
## name: Kanagawa
|
||||
## name: ${repoTheme.displayName}
|
||||
## license: MIT
|
||||
## author: Tommaso Laurenzi
|
||||
## upstream: https://github.com/rebelot/kanagawa.nvim/
|
||||
|
||||
background #1F1F28
|
||||
foreground #DCD7BA
|
||||
selection_background #2D4F67
|
||||
selection_foreground #C8C093
|
||||
url_color #72A7BC
|
||||
cursor #C8C093
|
||||
background ${palette.background}
|
||||
foreground ${palette.foreground}
|
||||
selection_background ${palette.selectionBackground}
|
||||
selection_foreground ${palette.selectionForeground}
|
||||
url_color ${palette.url}
|
||||
cursor ${palette.cursor}
|
||||
|
||||
active_tab_background #1F1F28
|
||||
active_tab_foreground #C8C093
|
||||
inactive_tab_background #1F1F28
|
||||
inactive_tab_foreground #727169
|
||||
active_tab_background ${palette.background}
|
||||
active_tab_foreground ${palette.selectionForeground}
|
||||
inactive_tab_background ${palette.background}
|
||||
inactive_tab_foreground ${palette.muted}
|
||||
|
||||
color0 #16161D
|
||||
color1 #C34043
|
||||
color2 #76946A
|
||||
color3 #C0A36E
|
||||
color4 #7E9CD8
|
||||
color5 #957FB8
|
||||
color6 #6A9589
|
||||
color7 #C8C093
|
||||
color0 ${palette.terminal.color0}
|
||||
color1 ${palette.terminal.color1}
|
||||
color2 ${palette.terminal.color2}
|
||||
color3 ${palette.terminal.color3}
|
||||
color4 ${palette.terminal.color4}
|
||||
color5 ${palette.terminal.color5}
|
||||
color6 ${palette.terminal.color6}
|
||||
color7 ${palette.terminal.color7}
|
||||
|
||||
color8 #727169
|
||||
color9 #E82424
|
||||
color10 #98BB6C
|
||||
color11 #E6C384
|
||||
color12 #7FB4CA
|
||||
color13 #938AA9
|
||||
color14 #7AA89F
|
||||
color15 #DCD7BA
|
||||
color8 ${palette.terminal.color8}
|
||||
color9 ${palette.terminal.color9}
|
||||
color10 ${palette.terminal.color10}
|
||||
color11 ${palette.terminal.color11}
|
||||
color12 ${palette.terminal.color12}
|
||||
color13 ${palette.terminal.color13}
|
||||
color14 ${palette.terminal.color14}
|
||||
color15 ${palette.terminal.color15}
|
||||
|
||||
color16 #FFA066
|
||||
color17 #FF5D62
|
||||
color16 ${palette.terminal.color16}
|
||||
color17 ${palette.terminal.color17}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
+29
-15
@@ -1,3 +1,11 @@
|
||||
{
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
metaLib = config.meta.lib;
|
||||
metaRepo = metaLib.repo;
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.theme =
|
||||
{
|
||||
@@ -5,10 +13,12 @@
|
||||
...
|
||||
}:
|
||||
let
|
||||
cursorTheme = {
|
||||
name = "phinger-cursors-light";
|
||||
package = pkgs.phinger-cursors;
|
||||
size = 24;
|
||||
repoTheme = metaRepo.theme;
|
||||
cursorTheme = repoTheme.cursor // {
|
||||
package = metaLib.resolvePackagePath {
|
||||
inherit pkgs;
|
||||
path = repoTheme.cursor.packagePath;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
@@ -25,21 +35,25 @@
|
||||
flake.modules.homeManager.theme =
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
cursorTheme = {
|
||||
name = "phinger-cursors-light";
|
||||
package = pkgs.phinger-cursors;
|
||||
size = 24;
|
||||
repoTheme = metaRepo.theme;
|
||||
cursorTheme = repoTheme.cursor // {
|
||||
package = metaLib.resolvePackagePath {
|
||||
inherit pkgs;
|
||||
path = repoTheme.cursor.packagePath;
|
||||
};
|
||||
};
|
||||
|
||||
kanagawaThemeSrc = pkgs.fetchFromGitHub {
|
||||
owner = "Fausto-Korpsvart";
|
||||
repo = "Kanagawa-GKT-Theme";
|
||||
rev = "55ca4ba249eba21f861b9866b71ab41bb8930318";
|
||||
hash = "sha256-UdMoMx2DoovcxSp/zBZ3PRv/Qpj+prd0uPm1gmdak2E=";
|
||||
inherit (repoTheme.kanagawa)
|
||||
hash
|
||||
owner
|
||||
repo
|
||||
rev
|
||||
;
|
||||
};
|
||||
|
||||
kanagawaOverride = {
|
||||
version = "unstable-2025-10-23";
|
||||
version = repoTheme.kanagawa.version;
|
||||
src = kanagawaThemeSrc;
|
||||
};
|
||||
in
|
||||
@@ -59,14 +73,14 @@
|
||||
"sftp://orion Orion VPS"
|
||||
];
|
||||
theme = {
|
||||
name = "Kanagawa-BL-LB";
|
||||
name = repoTheme.kanagawa.gtkThemeName;
|
||||
package = pkgs.kanagawa-gtk-theme.overrideAttrs (_: kanagawaOverride);
|
||||
};
|
||||
gtk4.theme = {
|
||||
inherit (config.gtk.theme) name package;
|
||||
};
|
||||
iconTheme = {
|
||||
name = "Kanagawa";
|
||||
name = repoTheme.kanagawa.iconThemeName;
|
||||
package = pkgs.kanagawa-icon-theme.overrideAttrs (_: kanagawaOverride);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,44 +1,58 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
metaRepo = config.meta.lib.repo;
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager.vicinae =
|
||||
{ pkgs, inputs, ... }:
|
||||
{
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
repoTheme = metaRepo.theme.kanagawa;
|
||||
palette = repoTheme.palette;
|
||||
in
|
||||
{
|
||||
programs.vicinae = {
|
||||
enable = true;
|
||||
systemd.enable = true;
|
||||
|
||||
themes.kanagawa-wave = {
|
||||
themes.${repoTheme.name} = {
|
||||
meta = {
|
||||
version = 1;
|
||||
name = "Kanagawa Wave";
|
||||
name = repoTheme.displayName;
|
||||
description = "A dark theme inspired by the colors of the famous painting by Katsushika Hokusai.";
|
||||
variant = "dark";
|
||||
inherits = "vicinae-dark";
|
||||
};
|
||||
colors = {
|
||||
core = {
|
||||
background = "#1F1F28";
|
||||
foreground = "#DCD7BA";
|
||||
secondary_background = "#16161D";
|
||||
border = "#2A2A37";
|
||||
accent = "#7E9CD8";
|
||||
background = palette.background;
|
||||
foreground = palette.foreground;
|
||||
secondary_background = palette.secondaryBackground;
|
||||
border = palette.border;
|
||||
accent = palette.accents.blue;
|
||||
};
|
||||
accents = {
|
||||
blue = "#7E9CD8";
|
||||
green = "#98BB6C";
|
||||
magenta = "#D27E99";
|
||||
orange = "#FFA066";
|
||||
purple = "#957FB8";
|
||||
red = "#E82424";
|
||||
yellow = "#E6C384";
|
||||
cyan = "#7AA89F";
|
||||
inherit (palette.accents)
|
||||
blue
|
||||
cyan
|
||||
green
|
||||
magenta
|
||||
orange
|
||||
purple
|
||||
red
|
||||
yellow
|
||||
;
|
||||
};
|
||||
input.border_focus = "colors.core.accent";
|
||||
};
|
||||
};
|
||||
|
||||
settings.theme = {
|
||||
light.name = "kanagawa-wave";
|
||||
dark.name = "kanagawa-wave";
|
||||
light.name = repoTheme.name;
|
||||
dark.name = repoTheme.name;
|
||||
};
|
||||
|
||||
extensions = with inputs.vicinae-extensions.packages.${pkgs.stdenv.hostPlatform.system}; [
|
||||
|
||||
@@ -15,7 +15,6 @@ in
|
||||
nixosModules.networking
|
||||
nixosModules.niri
|
||||
nixosModules.printing
|
||||
nixosModules.qbittorrent-client
|
||||
nixosModules.sddm
|
||||
nixosModules.sops-admin-key-file
|
||||
nixosModules.standard-boot
|
||||
@@ -30,18 +29,27 @@ in
|
||||
environment.localBinInPath = true;
|
||||
};
|
||||
|
||||
flake.modules.nixos.workstation-host = {
|
||||
imports = [
|
||||
nixosModules.workstation-base
|
||||
nixosModules.qbittorrent-client
|
||||
];
|
||||
};
|
||||
|
||||
flake.modules.homeManager.workstation-base = {
|
||||
imports = [
|
||||
homeModules.ai
|
||||
homeModules.bitwarden
|
||||
homeModules.clipboard
|
||||
homeModules.dev-tools
|
||||
homeModules.email
|
||||
homeModules.local-apps
|
||||
homeModules.mpv
|
||||
homeModules.neovim
|
||||
homeModules.nh
|
||||
homeModules.niri
|
||||
homeModules.nix
|
||||
homeModules.personal-productivity
|
||||
homeModules.pim
|
||||
homeModules.podman
|
||||
homeModules.shell
|
||||
homeModules.sops
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
metaLib = config.meta.lib;
|
||||
metaRepo = metaLib.repo;
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager.xdg =
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
browserPackage = metaLib.resolvePackagePath {
|
||||
inherit pkgs;
|
||||
path = metaRepo.desktop.browser.packagePath;
|
||||
};
|
||||
fileManagerPackage = metaLib.resolvePackagePath {
|
||||
inherit pkgs;
|
||||
path = metaRepo.desktop.fileManager.packagePath;
|
||||
};
|
||||
homeDir = config.home.homeDirectory;
|
||||
localDir = "${homeDir}/.local";
|
||||
mediaDir = "${homeDir}/media";
|
||||
@@ -33,12 +46,16 @@
|
||||
|
||||
mimeApps = {
|
||||
enable = true;
|
||||
defaultApplicationPackages = with pkgs; [
|
||||
defaultApplicationPackages =
|
||||
with pkgs;
|
||||
[
|
||||
sioyek
|
||||
imv
|
||||
vivaldi
|
||||
neovim
|
||||
nautilus
|
||||
]
|
||||
++ [
|
||||
browserPackage
|
||||
fileManagerPackage
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
+13
-16
@@ -4,29 +4,26 @@
|
||||
...
|
||||
}:
|
||||
let
|
||||
hostNames = builtins.attrNames (
|
||||
inputs.nixpkgs.lib.filterAttrs (_: type: type == "directory") (builtins.readDir ./hosts)
|
||||
);
|
||||
nixosModules = config.flake.modules.nixos;
|
||||
in
|
||||
{
|
||||
imports = [ inputs.flake-parts.flakeModules.modules ];
|
||||
imports = [
|
||||
inputs.flake-parts.flakeModules.modules
|
||||
./data.nix
|
||||
];
|
||||
|
||||
systems = [ "x86_64-linux" ];
|
||||
|
||||
flake.nixosConfigurations = {
|
||||
orion = inputs.nixpkgs.lib.nixosSystem {
|
||||
flake.nixosConfigurations = inputs.nixpkgs.lib.genAttrs hostNames (
|
||||
name:
|
||||
inputs.nixpkgs.lib.nixosSystem {
|
||||
specialArgs = { inherit inputs; };
|
||||
modules = [ nixosModules.orion ];
|
||||
};
|
||||
|
||||
polaris = inputs.nixpkgs.lib.nixosSystem {
|
||||
specialArgs = { inherit inputs; };
|
||||
modules = [ nixosModules.polaris ];
|
||||
};
|
||||
|
||||
zenith = inputs.nixpkgs.lib.nixosSystem {
|
||||
specialArgs = { inherit inputs; };
|
||||
modules = [ nixosModules.zenith ];
|
||||
};
|
||||
};
|
||||
modules = [ nixosModules.${name} ];
|
||||
}
|
||||
);
|
||||
|
||||
perSystem =
|
||||
{ pkgs, ... }:
|
||||
|
||||
@@ -17,22 +17,15 @@ in
|
||||
...
|
||||
}:
|
||||
let
|
||||
terminalPackage = metaLib.resolvePackagePath {
|
||||
terminal = metaLib.resolveRepoTerminal {
|
||||
inherit pkgs;
|
||||
path = config.meta.host.users.kiri.terminalPackagePath;
|
||||
};
|
||||
in
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = terminalPackage != null;
|
||||
message = "Unknown terminal package `${lib.showAttrPath config.meta.host.users.kiri.terminalPackagePath}` for user `kiri`.";
|
||||
}
|
||||
{
|
||||
assertion = terminalPackage != null && lib.elem "terminfo" terminalPackage.outputs;
|
||||
message = "Terminal package `${lib.showAttrPath config.meta.host.users.kiri.terminalPackagePath}` must provide a `terminfo` output for `orion`.";
|
||||
}
|
||||
];
|
||||
assertions = metaLib.mkTerminalAssertions {
|
||||
inherit terminal;
|
||||
requireTerminfo = true;
|
||||
};
|
||||
|
||||
users.users.kiri = {
|
||||
linger = true;
|
||||
@@ -43,15 +36,20 @@ in
|
||||
|
||||
environment.systemPackages = [
|
||||
]
|
||||
++ lib.optional (terminalPackage != null && lib.elem "terminfo" terminalPackage.outputs) (
|
||||
lib.getOutput "terminfo" terminalPackage
|
||||
);
|
||||
++ lib.optional terminal.hasTerminfo (lib.getOutput "terminfo" terminal.package);
|
||||
};
|
||||
|
||||
flake.modules.nixos.orion = metaLib.mkHost {
|
||||
name = "orion";
|
||||
users = {
|
||||
inherit (metaLib.users) kiri;
|
||||
kiri = {
|
||||
account = metaLib.accounts.kiri;
|
||||
homeImports = [
|
||||
homeModules.shell
|
||||
homeModules.git
|
||||
homeModules.syncthing
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
@@ -66,15 +64,6 @@ in
|
||||
nixosModules.radicale
|
||||
nixosModules.actual
|
||||
nixosModules.gitea
|
||||
(metaLib.mkHostUser {
|
||||
account = metaLib.users.kiri;
|
||||
needsPassword = false;
|
||||
homeImports = [
|
||||
homeModules.shell
|
||||
homeModules.git
|
||||
homeModules.syncthing
|
||||
];
|
||||
})
|
||||
./_hardware.nix
|
||||
./_disk.nix
|
||||
];
|
||||
|
||||
@@ -7,6 +7,13 @@ let
|
||||
nixosModules = config.flake.modules.nixos;
|
||||
homeModules = config.flake.modules.homeManager;
|
||||
metaLib = config.meta.lib;
|
||||
workstationHomeImports = [ homeModules.workstation-base ];
|
||||
kiriHomeImports = workstationHomeImports ++ [
|
||||
homeModules.syncthing
|
||||
homeModules.qbittorrent-client
|
||||
homeModules.noctalia
|
||||
];
|
||||
ergonHomeImports = workstationHomeImports ++ [ homeModules.noctalia ];
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.polaris = metaLib.mkHost {
|
||||
@@ -29,41 +36,23 @@ in
|
||||
mouse.accelSpeed = 0.4;
|
||||
};
|
||||
|
||||
sourceControl.users = {
|
||||
kiri.personal.publicKey = "";
|
||||
users = {
|
||||
kiri = {
|
||||
account = metaLib.accounts.kiri;
|
||||
needsPassword = true;
|
||||
homeImports = kiriHomeImports;
|
||||
};
|
||||
|
||||
ergon = {
|
||||
personal.publicKey = "";
|
||||
work.publicKey = "";
|
||||
account = metaLib.accounts.ergon;
|
||||
needsPassword = true;
|
||||
homeImports = ergonHomeImports;
|
||||
};
|
||||
};
|
||||
|
||||
users = {
|
||||
inherit (metaLib.users)
|
||||
ergon
|
||||
kiri
|
||||
;
|
||||
};
|
||||
|
||||
imports = [
|
||||
nixosModules.workstation-base
|
||||
nixosModules.workstation-host
|
||||
nixosModules.steam
|
||||
(metaLib.mkHostUser {
|
||||
account = metaLib.users.kiri;
|
||||
needsPassword = true;
|
||||
homeImports = [
|
||||
homeModules.kiri-workstation
|
||||
homeModules.noctalia
|
||||
];
|
||||
})
|
||||
(metaLib.mkHostUser {
|
||||
account = metaLib.users.ergon;
|
||||
needsPassword = true;
|
||||
homeImports = [
|
||||
homeModules.ergon-workstation
|
||||
homeModules.noctalia
|
||||
];
|
||||
})
|
||||
./_hardware.nix
|
||||
]
|
||||
++ (with inputs.nixos-hardware.nixosModules; [
|
||||
|
||||
@@ -7,6 +7,14 @@ let
|
||||
nixosModules = config.flake.modules.nixos;
|
||||
homeModules = config.flake.modules.homeManager;
|
||||
metaLib = config.meta.lib;
|
||||
workstationHomeImports = [ homeModules.workstation-base ];
|
||||
portableNoctalia = homeModules.noctalia-portable;
|
||||
kiriHomeImports = workstationHomeImports ++ [
|
||||
homeModules.syncthing
|
||||
homeModules.qbittorrent-client
|
||||
portableNoctalia
|
||||
];
|
||||
ergonHomeImports = workstationHomeImports ++ [ portableNoctalia ];
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.zenith = metaLib.mkHost {
|
||||
@@ -31,8 +39,6 @@ in
|
||||
};
|
||||
|
||||
sourceControl.users = {
|
||||
kiri.personal.publicKey = "";
|
||||
|
||||
ergon = {
|
||||
personal.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPdR3KP2U84i7f7MlRqcML/3YyMw8JL3hdm637SkMUwO ergon@zenith#personal";
|
||||
work.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIHJz5uHKm0/TiMNh/cmzrODHNZ8NgEEZe+47XnJwQGk ergon@zenith#work";
|
||||
@@ -40,31 +46,22 @@ in
|
||||
};
|
||||
|
||||
users = {
|
||||
inherit (metaLib.users)
|
||||
ergon
|
||||
kiri
|
||||
;
|
||||
kiri = {
|
||||
account = metaLib.accounts.kiri;
|
||||
needsPassword = true;
|
||||
homeImports = kiriHomeImports;
|
||||
};
|
||||
|
||||
ergon = {
|
||||
account = metaLib.accounts.ergon;
|
||||
needsPassword = true;
|
||||
homeImports = ergonHomeImports;
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
nixosModules.workstation-base
|
||||
nixosModules.workstation-host
|
||||
nixosModules.laptop-power
|
||||
(metaLib.mkHostUser {
|
||||
account = metaLib.users.kiri;
|
||||
needsPassword = true;
|
||||
homeImports = [
|
||||
homeModules.kiri-workstation
|
||||
homeModules.noctalia-portable
|
||||
];
|
||||
})
|
||||
(metaLib.mkHostUser {
|
||||
account = metaLib.users.ergon;
|
||||
needsPassword = true;
|
||||
homeImports = [
|
||||
homeModules.ergon-workstation
|
||||
homeModules.noctalia-portable
|
||||
];
|
||||
})
|
||||
{
|
||||
hardware.enableRedistributableFirmware = true;
|
||||
services.fwupd.enable = true;
|
||||
|
||||
+254
-50
@@ -4,6 +4,15 @@
|
||||
...
|
||||
}:
|
||||
let
|
||||
normalizeHostUser =
|
||||
spec:
|
||||
{
|
||||
homeImports = [ ];
|
||||
needsPassword = false;
|
||||
stateVersion = null;
|
||||
}
|
||||
// spec;
|
||||
|
||||
mkHost =
|
||||
{
|
||||
name,
|
||||
@@ -14,6 +23,16 @@ let
|
||||
imports ? [ ],
|
||||
stateVersion ? "24.05",
|
||||
}:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
hostUserSpecs = lib.mapAttrs (_: spec: normalizeHostUser spec) users;
|
||||
hostUsers = lib.mapAttrs (_: spec: spec.account) hostUserSpecs;
|
||||
passwordUserSpecs = lib.filterAttrs (_: spec: spec.needsPassword) hostUserSpecs;
|
||||
in
|
||||
{
|
||||
meta.host = {
|
||||
inherit
|
||||
@@ -21,14 +40,53 @@ let
|
||||
input
|
||||
name
|
||||
sourceControl
|
||||
users
|
||||
;
|
||||
users = hostUsers;
|
||||
};
|
||||
|
||||
inherit imports;
|
||||
|
||||
networking.hostName = name;
|
||||
system.stateVersion = stateVersion;
|
||||
|
||||
programs.zsh.enable = true;
|
||||
|
||||
sops.secrets = lib.mapAttrs' (
|
||||
userName: _:
|
||||
lib.nameValuePair "hashed-password-${userName}" {
|
||||
neededForUsers = true;
|
||||
}
|
||||
) passwordUserSpecs;
|
||||
|
||||
users.users = lib.mapAttrs (
|
||||
userName: spec:
|
||||
{
|
||||
name = userName;
|
||||
home = spec.account.homeDirectory;
|
||||
isNormalUser = true;
|
||||
shell = pkgs.zsh;
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"networkmanager"
|
||||
];
|
||||
}
|
||||
// lib.optionalAttrs spec.needsPassword {
|
||||
hashedPasswordFile = config.sops.secrets."hashed-password-${userName}".path;
|
||||
}
|
||||
) hostUserSpecs;
|
||||
|
||||
home-manager.users = lib.mapAttrs (_: spec: {
|
||||
imports = spec.homeImports;
|
||||
meta = {
|
||||
host = config.meta.host;
|
||||
user = spec.account;
|
||||
};
|
||||
home = {
|
||||
username = spec.account.name;
|
||||
homeDirectory = spec.account.homeDirectory;
|
||||
stateVersion = if spec.stateVersion == null then stateVersion else spec.stateVersion;
|
||||
};
|
||||
}) hostUserSpecs;
|
||||
};
|
||||
|
||||
mkCaddyReverseProxy =
|
||||
@@ -65,62 +123,199 @@ let
|
||||
}:
|
||||
lib.attrByPath path null pkgs;
|
||||
|
||||
mkHostUser =
|
||||
resolveRepoTerminal =
|
||||
{
|
||||
account,
|
||||
homeImports,
|
||||
needsPassword ? false,
|
||||
stateVersion ? "24.05",
|
||||
}:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
name = account.name;
|
||||
primaryEmails = lib.filter (email: email.primary) (builtins.attrValues account.emails);
|
||||
terminal = config.meta.lib.repo.desktop.terminal;
|
||||
package = resolvePackagePath {
|
||||
inherit pkgs;
|
||||
path = terminal.packagePath;
|
||||
};
|
||||
in
|
||||
{
|
||||
assertions = [
|
||||
inherit
|
||||
package
|
||||
;
|
||||
inherit (terminal)
|
||||
command
|
||||
desktopId
|
||||
packagePath
|
||||
;
|
||||
|
||||
hasPackage = package != null;
|
||||
|
||||
hasDesktopEntry =
|
||||
package != null && builtins.pathExists "${package}/share/applications/${terminal.desktopId}";
|
||||
|
||||
hasTerminfo = package != null && lib.elem "terminfo" package.outputs;
|
||||
};
|
||||
|
||||
mkTerminalAssertions =
|
||||
{
|
||||
assertion = builtins.length primaryEmails == 1;
|
||||
message = "User ${name} must define exactly one primary email entry.";
|
||||
terminal,
|
||||
requireDesktopEntry ? false,
|
||||
requireTerminfo ? false,
|
||||
}:
|
||||
lib.flatten [
|
||||
[
|
||||
{
|
||||
assertion = terminal.hasPackage;
|
||||
message = "Unknown terminal package `${lib.showAttrPath terminal.packagePath}` in `meta.lib.repo.desktop.terminal`.";
|
||||
}
|
||||
{
|
||||
assertion = terminal.command == "kitty";
|
||||
message = "The repo terminal is currently expected to be kitty.";
|
||||
}
|
||||
]
|
||||
(lib.optional requireDesktopEntry {
|
||||
assertion = terminal.hasDesktopEntry;
|
||||
message = "Terminal package `${lib.showAttrPath terminal.packagePath}` must provide `${terminal.desktopId}`.";
|
||||
})
|
||||
(lib.optional requireTerminfo {
|
||||
assertion = terminal.hasTerminfo;
|
||||
message = "Terminal package `${lib.showAttrPath terminal.packagePath}` must provide a `terminfo` output.";
|
||||
})
|
||||
];
|
||||
|
||||
programs.zsh.enable = true;
|
||||
mapConfiguredAttrs =
|
||||
{
|
||||
settings,
|
||||
schema,
|
||||
}:
|
||||
lib.filterAttrs (_: value: value != null) (
|
||||
lib.mapAttrs (
|
||||
_: spec:
|
||||
let
|
||||
value = lib.getAttrFromPath spec.path settings;
|
||||
in
|
||||
if value == null then
|
||||
null
|
||||
else if spec ? values then
|
||||
spec.values.${value}
|
||||
else if spec ? transform then
|
||||
spec.transform value
|
||||
else
|
||||
value
|
||||
) schema
|
||||
);
|
||||
|
||||
sops.secrets = lib.optionalAttrs needsPassword {
|
||||
"hashed-password-${name}".neededForUsers = true;
|
||||
mkInputProfiles =
|
||||
input:
|
||||
let
|
||||
scrollMethodValues = {
|
||||
edge = {
|
||||
libinput = "edge";
|
||||
niri = "edge";
|
||||
};
|
||||
"no-scroll" = {
|
||||
libinput = "none";
|
||||
niri = "no-scroll";
|
||||
};
|
||||
"on-button-down" = {
|
||||
libinput = "button";
|
||||
niri = "on-button-down";
|
||||
};
|
||||
"two-finger" = {
|
||||
libinput = "twofinger";
|
||||
niri = "two-finger";
|
||||
};
|
||||
};
|
||||
|
||||
users.users.${name} = {
|
||||
name = account.name;
|
||||
home = account.homeDirectory;
|
||||
isNormalUser = true;
|
||||
shell = pkgs.zsh;
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"networkmanager"
|
||||
];
|
||||
}
|
||||
// lib.optionalAttrs needsPassword {
|
||||
hashedPasswordFile = config.sops.secrets."hashed-password-${name}".path;
|
||||
mouse = input.mouse;
|
||||
touchpad = input.touchpad;
|
||||
|
||||
libinputMouse = mapConfiguredAttrs {
|
||||
settings = mouse;
|
||||
schema = {
|
||||
accelProfile.path = [ "accelProfile" ];
|
||||
accelSpeed = {
|
||||
path = [ "accelSpeed" ];
|
||||
transform = toString;
|
||||
};
|
||||
leftHanded.path = [ "leftHanded" ];
|
||||
middleEmulation.path = [ "middleEmulation" ];
|
||||
naturalScrolling.path = [ "naturalScrolling" ];
|
||||
scrollMethod = {
|
||||
path = [ "scrollMethod" ];
|
||||
values = lib.mapAttrs (_: value: value.libinput) scrollMethodValues;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
home-manager.users.${name} = {
|
||||
imports = homeImports;
|
||||
meta = {
|
||||
host = config.meta.host;
|
||||
user = account;
|
||||
libinputTouchpad = mapConfiguredAttrs {
|
||||
settings = touchpad;
|
||||
schema = {
|
||||
accelProfile.path = [ "accelProfile" ];
|
||||
accelSpeed = {
|
||||
path = [ "accelSpeed" ];
|
||||
transform = toString;
|
||||
};
|
||||
home = {
|
||||
username = account.name;
|
||||
homeDirectory = account.homeDirectory;
|
||||
inherit stateVersion;
|
||||
clickMethod = {
|
||||
path = [ "clickMethod" ];
|
||||
values = {
|
||||
"button-areas" = "buttonareas";
|
||||
clickfinger = "clickfinger";
|
||||
};
|
||||
};
|
||||
disableWhileTyping.path = [ "disableWhileTyping" ];
|
||||
leftHanded.path = [ "leftHanded" ];
|
||||
middleEmulation.path = [ "middleEmulation" ];
|
||||
naturalScrolling.path = [ "naturalScrolling" ];
|
||||
scrollMethod = {
|
||||
path = [ "scrollMethod" ];
|
||||
values = lib.mapAttrs (_: value: value.libinput) scrollMethodValues;
|
||||
};
|
||||
tapping.path = [ "tapping" ];
|
||||
};
|
||||
};
|
||||
|
||||
niriMouse = mapConfiguredAttrs {
|
||||
settings = mouse;
|
||||
schema = {
|
||||
"accel-profile".path = [ "accelProfile" ];
|
||||
"accel-speed".path = [ "accelSpeed" ];
|
||||
"left-handed".path = [ "leftHanded" ];
|
||||
"middle-emulation".path = [ "middleEmulation" ];
|
||||
"natural-scroll".path = [ "naturalScrolling" ];
|
||||
"scroll-method" = {
|
||||
path = [ "scrollMethod" ];
|
||||
values = lib.mapAttrs (_: value: value.niri) scrollMethodValues;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
niriTouchpad = mapConfiguredAttrs {
|
||||
settings = touchpad;
|
||||
schema = {
|
||||
"accel-profile".path = [ "accelProfile" ];
|
||||
"accel-speed".path = [ "accelSpeed" ];
|
||||
"click-method".path = [ "clickMethod" ];
|
||||
dwt.path = [ "disableWhileTyping" ];
|
||||
"left-handed".path = [ "leftHanded" ];
|
||||
"middle-emulation".path = [ "middleEmulation" ];
|
||||
"natural-scroll".path = [ "naturalScrolling" ];
|
||||
"scroll-method" = {
|
||||
path = [ "scrollMethod" ];
|
||||
values = lib.mapAttrs (_: value: value.niri) scrollMethodValues;
|
||||
};
|
||||
tap.path = [ "tapping" ];
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
hasPointerConfig = libinputMouse != { } || libinputTouchpad != { };
|
||||
|
||||
libinput = {
|
||||
mouse = libinputMouse;
|
||||
touchpad = libinputTouchpad;
|
||||
};
|
||||
|
||||
niri = {
|
||||
mouse = niriMouse;
|
||||
touchpad = niriTouchpad;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
@@ -138,13 +333,6 @@ in
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
options.meta.lib.mkHostUser = lib.mkOption {
|
||||
type = lib.types.raw;
|
||||
description = "Internal helper for explicit per-host user assembly.";
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
options.meta.lib.resolvePackagePath = lib.mkOption {
|
||||
type = lib.types.raw;
|
||||
description = "Internal helper to resolve package attr paths against the local pkgs set.";
|
||||
@@ -152,19 +340,35 @@ in
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
options.meta.lib.users = lib.mkOption {
|
||||
type = lib.types.attrs;
|
||||
description = "Canonical user attrsets shared by host definitions.";
|
||||
options.meta.lib.resolveRepoTerminal = lib.mkOption {
|
||||
type = lib.types.raw;
|
||||
description = "Internal helper to resolve and validate the repo-standard terminal.";
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
options.meta.lib.mkTerminalAssertions = lib.mkOption {
|
||||
type = lib.types.raw;
|
||||
description = "Internal helper for terminal-related assertions shared across modules.";
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
options.meta.lib.mkInputProfiles = lib.mkOption {
|
||||
type = lib.types.raw;
|
||||
description = "Internal helper to normalize host input metadata for libinput and Niri.";
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
config.meta.lib = {
|
||||
inherit
|
||||
mkInputProfiles
|
||||
mkCaddyReverseProxy
|
||||
mkTerminalAssertions
|
||||
mkHost
|
||||
mkHostUser
|
||||
resolvePackagePath
|
||||
resolveRepoTerminal
|
||||
;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
{ ... }:
|
||||
let
|
||||
kiri = {
|
||||
name = "kiri";
|
||||
realName = "Jelle Spreeuwenberg";
|
||||
homeDirectory = "/home/kiri";
|
||||
terminalPackagePath = [ "kitty" ];
|
||||
emails = {
|
||||
personal = {
|
||||
address = "mail@jelles.net";
|
||||
primary = true;
|
||||
type = "mxrouting";
|
||||
};
|
||||
old = {
|
||||
address = "mail@jellespreeuwenberg.nl";
|
||||
primary = false;
|
||||
type = "mxrouting";
|
||||
};
|
||||
uni = {
|
||||
address = "j.spreeuwenberg@student.tue.nl";
|
||||
primary = false;
|
||||
type = "office365";
|
||||
};
|
||||
work = {
|
||||
address = "jelle.spreeuwenberg@yookr.org";
|
||||
primary = false;
|
||||
type = "office365";
|
||||
};
|
||||
};
|
||||
sourceControl = {
|
||||
profiles = {
|
||||
github-personal = { };
|
||||
gitlab-personal = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ergon = {
|
||||
name = "ergon";
|
||||
realName = "Jelle Spreeuwenberg";
|
||||
homeDirectory = "/home/ergon";
|
||||
terminalPackagePath = [ "kitty" ];
|
||||
emails = {
|
||||
personal = {
|
||||
address = "mail@jelles.net";
|
||||
primary = false;
|
||||
type = "mxrouting";
|
||||
};
|
||||
work = {
|
||||
address = "jelle.spreeuwenberg@yookr.org";
|
||||
primary = true;
|
||||
type = "office365";
|
||||
};
|
||||
};
|
||||
sourceControl = {
|
||||
profiles = {
|
||||
github-personal = { };
|
||||
github-work = { };
|
||||
gitlab-personal = { };
|
||||
};
|
||||
|
||||
projectScope = "work";
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
meta.lib.users = {
|
||||
inherit
|
||||
ergon
|
||||
kiri
|
||||
;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user