Compare commits

..

187 Commits

Author SHA1 Message Date
7b24ac3edb locked linux-firmware and 6.12.52 2025-11-23 11:17:36 -05:00
github-actions[bot]
27565173d4 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/0d05242?dir=pkgs/firefox-addons' (2025-11-20)
  → 'gitlab:rycee/nur-expressions/cefce78?dir=pkgs/firefox-addons' (2025-11-21)
• Updated input 'home-manager':
    'github:nix-community/home-manager/ea164b7' (2025-11-17)
  → 'github:nix-community/home-manager/d10a9b1' (2025-11-21)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/973aa8b' (2025-11-21)
  → 'github:nixos/nixpkgs/8a7cf7e' (2025-11-22)
2025-11-22 09:49:19 -05:00
0c0ed92cb4 fixing user git settings 2025-11-20 21:36:54 -05:00
github-actions[bot]
cc9996d6fa flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/d84c9ea?dir=pkgs/firefox-addons' (2025-11-14)
  → 'gitlab:rycee/nur-expressions/0d05242?dir=pkgs/firefox-addons' (2025-11-20)
• Updated input 'home-manager':
    'github:nix-community/home-manager/827f2a2' (2025-11-12)
  → 'github:nix-community/home-manager/ea164b7' (2025-11-17)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/c5ae371' (2025-11-12)
  → 'github:nixos/nixpkgs/89c2b23' (2025-11-17)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/20f91b6' (2025-11-15)
  → 'github:nixos/nixpkgs/973aa8b' (2025-11-21)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/a2bcd1c' (2025-11-13)
  → 'github:Mic92/sops-nix/877bb49' (2025-11-20)
2025-11-20 21:30:32 -05:00
102f36eb1b adding post quantum ssh KexAlgorithms 2025-11-16 08:22:41 -05:00
9ec988729b testing zfs_unstable and linuxPackages_6_17 2025-11-15 13:47:13 -05:00
4e3c25afb4 added draw io setting 2025-11-15 13:47:13 -05:00
0d482aca4b HA update 2025-11-15 13:47:13 -05:00
github-actions[bot]
c624781d84 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/5cca27f1bb30a26140d0cf60ab34daa45b4fa11f?dir=pkgs/firefox-addons&narHash=sha256-h%2BliPhhMw1yYvkDGLHzQJQShQs%2ByLjNgjfAyZX%2BsRrM%3D' (2025-10-17)
  → 'gitlab:rycee/nur-expressions/d84c9ea299c1e4629f0d0716799f5c57975021ce?dir=pkgs/firefox-addons&narHash=sha256-FjnOyxTNNt85ZNjtLqRnG23LKQyvilGzyrO0bLffMm8%3D' (2025-11-14)
• Updated input 'home-manager':
    'github:nix-community/home-manager/722792af097dff5790f1a66d271a47759f477755?narHash=sha256-mlDqR1Ntgs9uYYEAUR1IhamKBO0lxoNS4zGLzEZaY0A%3D' (2025-10-17)
  → 'github:nix-community/home-manager/827f2a23373a774a8805f84ca5344654c31f354b?narHash=sha256-RYHN8O/Aja59XDji6WSJZPkJpYVUfpSkyH%2BPEupBJqM%3D' (2025-11-12)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/9ed85f8afebf2b7478f25db0a98d0e782c0ed903?narHash=sha256-2GoxVaKWTHBxRoeUYSjv0AfSOx4qw5CWSFz2b%2BVolKU%3D' (2025-10-10)
  → 'github:nixos/nixos-hardware/899dc449bc6428b9ee6b3b8f771ca2b0ef945ab9?narHash=sha256-BWWnUUT01lPwCWUvS0p6Px5UOBFeXJ8jR%2BZdLX8IbrU%3D' (2025-11-11)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/544961dfcce86422ba200ed9a0b00dd4b1486ec5?narHash=sha256-EVAqOteLBFmd7pKkb0%2BFIUyzTF61VKi7YmvP1tw4nEw%3D' (2025-10-15)
  → 'github:nixos/nixpkgs/c5ae371f1a6a7fd27823bc500d9390b38c05fa55?narHash=sha256-4PqRErxfe%2B2toFJFgcRKZ0UI9NSIOJa%2B7RXVtBhy4KE%3D' (2025-11-12)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/d85429339c0bcf0428084fe1306c970aed364417?narHash=sha256-1296zQfPiLZNrLKzX1t%2BkunadeI/mH82hKze3voduEI%3D' (2025-10-18)
  → 'github:nixos/nixpkgs/20f91b6ba5eff456057e359946c3e832173b18df?narHash=sha256-jVuaLD1Yf2aHILt2EedLFhqJnQXAS8kIo3P4LbtYDyg%3D' (2025-11-15)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/ab8d56e85b8be14cff9d93735951e30c3e86a437?narHash=sha256-8mN3kqyqa2PKY0wwZ2UmMEYMcxvNTwLaOrrDsw6Qi4E%3D' (2025-10-13)
  → 'github:Mic92/sops-nix/a2bcd1c25c1d29e22756ccae094032ab4ada2268?narHash=sha256-A91a%2BK0Q9wfdPLwL06e/kbHeAWSzPYy2EGdTDsyfb%2Bs%3D' (2025-11-13)
2025-11-15 13:47:13 -05:00
f4996b71e4 updating to inputs.self 2025-11-11 00:11:30 -05:00
58a29214d3 testing "${self}/ in imports 2025-11-11 00:11:30 -05:00
c4171b56b5 fixed systemd.nix 2025-11-10 23:21:54 -05:00
d6d48516ea updated PYTHONPATH to "${self}/" 2025-11-10 23:21:54 -05:00
ae882ba578 flake lock update 2025-11-10 23:21:54 -05:00
100b8145e8 fixed PYTHONPATH 2025-11-10 23:21:54 -05:00
e99cd8e54a removed system_tools 2025-11-10 23:21:54 -05:00
de9348432c deleted temp_flake.lock 2025-11-10 23:21:54 -05:00
b1fa596f37 fix victron_modbuss gps setting 2025-11-10 12:02:14 -05:00
908bccb8dc fixed typo 2025-11-08 08:21:27 -05:00
b8cc9c5772 testing dev shell for python pytest 2025-11-08 08:21:27 -05:00
d62076a900 added psycopg to my_python 2025-11-08 08:21:27 -05:00
0c9bd40659 fixed mypy and ruff errors 2025-11-08 08:21:27 -05:00
f713b8d4fa added safe_insert 2025-11-08 08:21:27 -05:00
ddba7d1068 disabling use_x_forwarded_for for brain HA 2025-11-08 07:11:30 -05:00
41aad90140 updated python.defaultInterpreterPath 2025-11-01 18:00:38 -04:00
76cd6e1188 adding osu-lazer for 2025-11-01 18:00:38 -04:00
20ef02b0cc removed n8n 2025-11-01 00:06:00 -04:00
c0e9f3f937 updated the first post 2025-11-01 00:02:02 -04:00
9e0a2810f5 adding baseurl and updating index.md 2025-11-01 00:02:02 -04:00
5c488422a1 added jekyll-paginate 2025-11-01 00:02:02 -04:00
9d43704b64 added gemfile inxed.md and fixed typo 2025-11-01 00:02:02 -04:00
d5bc6e9c6e Delete CNAME 2025-11-01 00:02:02 -04:00
17cebe1a82 Create CNAME 2025-11-01 00:02:02 -04:00
f02a866b19 updated theme 2025-11-01 00:02:02 -04:00
65c2bed046 testing blog 2025-11-01 00:02:02 -04:00
26cf123357 fixed ruff errors 2025-10-31 22:37:54 -04:00
a7c0a58c9a playing with logging 2025-10-31 22:37:54 -04:00
e89fb9fae1 mixed a format 2025-10-31 22:37:54 -04:00
44feda70c1 removed trusted_proxies from home_assistant 2025-10-31 22:37:54 -04:00
1bfdbfd785 fixed all ruff error in python 2025-10-31 22:37:54 -04:00
6a09bc66b6 fixed most ruff error 2025-10-31 22:37:54 -04:00
65fca5c8a4 added ruff the treefmt 2025-10-31 22:37:54 -04:00
a6e2334999 fixed systemd tests 2025-10-31 22:37:54 -04:00
c5981e0e6c testing python -m pytest tests 2025-10-31 22:37:54 -04:00
825672a450 added tests 2025-10-31 22:37:54 -04:00
d2db0de371 added my_python pkgs 2025-10-31 22:37:54 -04:00
8142582e4a disabled kafka 2025-10-31 22:37:54 -04:00
3038e1c704 updated cSpell words 2025-10-31 22:37:54 -04:00
18de5bc12c got python default.nix working 2025-10-31 22:37:54 -04:00
4fa2141461 added common and paralleize 2025-10-31 22:37:54 -04:00
626bd70d67 added installer 2025-10-31 22:37:54 -04:00
8ed7eda020 added tools 2025-10-31 22:37:54 -04:00
e9ae9478bf added random 2025-10-31 22:37:54 -04:00
c1ce7e0ac4 added zfs 2025-10-31 22:37:54 -04:00
d040b06869 added system_tests 2025-10-31 22:37:54 -04:00
04c41c6ac0 fixed typo 2025-10-31 20:10:46 -04:00
298adcce87 added charger to modbuss 2025-10-31 20:10:46 -04:00
ef25153c84 setting up esphome 2025-10-31 20:10:46 -04:00
9416bbd00b added gps data to victron_modbuss.yaml 2025-10-28 21:15:07 -04:00
b8200af6d5 updated victron_modbuss = "!include 2025-10-26 08:34:32 -04:00
afb62b97d1 got usb decrypt working 2025-10-26 08:34:32 -04:00
cf75f3d75a added esp home 2025-10-26 08:34:32 -04:00
0f8a594545 updated haproxy.cfg 2025-10-26 08:34:32 -04:00
db37eb2f9e added victron_modbuss.yaml 2025-10-26 08:34:32 -04:00
534d9110e2 added bluetooth to brain 2025-10-26 08:34:32 -04:00
86a1cac42c added forecast_solar 2025-10-26 08:34:32 -04:00
d3452dfab5 removed eleises ssh config 2025-10-26 08:34:32 -04:00
acab92ac9c updated brain home assistant 2025-10-26 08:34:32 -04:00
github-actions[bot]
f22a9d107a flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/7ca23f73a8caea22efaa6a298e0c505057722e42?dir=pkgs/firefox-addons&narHash=sha256-5PQfBVloDHMnWiuK4ir%2BawRYCCWWVC/pb5FdtBiroDQ%3D' (2025-10-10)
  → 'gitlab:rycee/nur-expressions/5cca27f1bb30a26140d0cf60ab34daa45b4fa11f?dir=pkgs/firefox-addons&narHash=sha256-h%2BliPhhMw1yYvkDGLHzQJQShQs%2ByLjNgjfAyZX%2BsRrM%3D' (2025-10-17)
• Updated input 'home-manager':
    'github:nix-community/home-manager/d305eece827a3fe317a2d70138f53feccaf890a1?narHash=sha256-GKMwBaFRw/C1p1VtjDz4DyhyzjKUWyi1K50bh8lgA2E%3D' (2025-10-10)
  → 'github:nix-community/home-manager/722792af097dff5790f1a66d271a47759f477755?narHash=sha256-mlDqR1Ntgs9uYYEAUR1IhamKBO0lxoNS4zGLzEZaY0A%3D' (2025-10-17)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/0b4defa2584313f3b781240b29d61f6f9f7e0df3?narHash=sha256-Oncbh0UmHjSlxO7ErQDM3KM0A5/Znfofj2BSzlHLeVw%3D' (2025-10-09)
  → 'github:nixos/nixpkgs/544961dfcce86422ba200ed9a0b00dd4b1486ec5?narHash=sha256-EVAqOteLBFmd7pKkb0%2BFIUyzTF61VKi7YmvP1tw4nEw%3D' (2025-10-15)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/fc430b0bdf9a58513b9f4607ef6f283f5c262e78?narHash=sha256-aQe1Yx9dsxVKQfAVI5dFHdQnFobnVMy6/wURXFoOKJ8%3D' (2025-10-11)
  → 'github:nixos/nixpkgs/d85429339c0bcf0428084fe1306c970aed364417?narHash=sha256-1296zQfPiLZNrLKzX1t%2BkunadeI/mH82hKze3voduEI%3D' (2025-10-18)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/6e5a38e08a2c31ae687504196a230ae00ea95133?narHash=sha256-UvzKi02LMFP74csFfwLPAZ0mrE7k6EiYaKecplyX9Qk%3D' (2025-10-05)
  → 'github:Mic92/sops-nix/ab8d56e85b8be14cff9d93735951e30c3e86a437?narHash=sha256-8mN3kqyqa2PKY0wwZ2UmMEYMcxvNTwLaOrrDsw6Qi4E%3D' (2025-10-13)
• Updated input 'system_tools':
    'github:RichieCahill/system_tools/db77c09c654e589fabb0d401c9bc48c1738e17e1?narHash=sha256-BmGGRq81ngdaosuYXDF6RYXdWLW7kd%2Bi7dy17WGTLio%3D' (2025-10-17)
  → 'github:RichieCahill/system_tools/a125c3e5c01cecbc3f2a842ffb1abb1210c35706?narHash=sha256-u/uciy9kpM/CBZKl05iAZRaOTwUHiuI0L/qbkk2mLUg%3D' (2025-10-18)
2025-10-17 22:05:18 -04:00
e5e0f883b0 updated system_tools 2025-10-17 18:20:27 -04:00
04bf6f2038 updated commands with typer 2025-10-17 18:20:27 -04:00
d35ba60c69 adding n8n 2025-10-17 12:38:12 -04:00
1e85635e89 updating ssh configs 2025-10-17 12:38:12 -04:00
6423192ee7 adding keyFile to brain 2025-10-17 12:29:46 -04:00
github-actions[bot]
a33aba3afc flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/df243385cb4a2263b4888687961ec1f2b5f641f3?dir=pkgs/firefox-addons&narHash=sha256-wy15H4AbfQpTWes4aoFaIxkvK6e9p0lyvypOsTMt5f0%3D' (2025-10-03)
  → 'gitlab:rycee/nur-expressions/7ca23f73a8caea22efaa6a298e0c505057722e42?dir=pkgs/firefox-addons&narHash=sha256-5PQfBVloDHMnWiuK4ir%2BawRYCCWWVC/pb5FdtBiroDQ%3D' (2025-10-10)
• Updated input 'home-manager':
    'github:nix-community/home-manager/edafd6da1936426708f1be0b1a4288007f16639a?narHash=sha256-0aXlKPxm2M%2BF5oywX2TTbY0e6h%2BtQ%2B6OYyx7UZn3A4A%3D' (2025-10-04)
  → 'github:nix-community/home-manager/d305eece827a3fe317a2d70138f53feccaf890a1?narHash=sha256-GKMwBaFRw/C1p1VtjDz4DyhyzjKUWyi1K50bh8lgA2E%3D' (2025-10-10)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/e087756cf4abbe1a34f3544c480fc1034d68742f?narHash=sha256-wPd5oGvBBpUEzMF0kWnXge0WITNsITx/aGI9qLHgJ4g%3D' (2025-09-30)
  → 'github:nixos/nixos-hardware/9ed85f8afebf2b7478f25db0a98d0e782c0ed903?narHash=sha256-2GoxVaKWTHBxRoeUYSjv0AfSOx4qw5CWSFz2b%2BVolKU%3D' (2025-10-10)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/7df7ff7d8e00218376575f0acdcc5d66741351ee?narHash=sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs%3D' (2025-10-02)
  → 'github:nixos/nixpkgs/0b4defa2584313f3b781240b29d61f6f9f7e0df3?narHash=sha256-Oncbh0UmHjSlxO7ErQDM3KM0A5/Znfofj2BSzlHLeVw%3D' (2025-10-09)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/27ac93958969b5f3dccd654b402599cf3de633ac?narHash=sha256-hhM8SUI6kQMei5TImFdNQy9EDT8g2hAD161DUtbfAy0%3D' (2025-10-04)
  → 'github:nixos/nixpkgs/fc430b0bdf9a58513b9f4607ef6f283f5c262e78?narHash=sha256-aQe1Yx9dsxVKQfAVI5dFHdQnFobnVMy6/wURXFoOKJ8%3D' (2025-10-11)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/9fcfabe085281dd793589bdc770a2e577a3caa5d?narHash=sha256-f9QC2KKiNReZDG2yyKAtDZh0rSK2Xp1wkPzKbHeQVRU%3D' (2025-09-29)
  → 'github:Mic92/sops-nix/6e5a38e08a2c31ae687504196a230ae00ea95133?narHash=sha256-UvzKi02LMFP74csFfwLPAZ0mrE7k6EiYaKecplyX9Qk%3D' (2025-10-05)
2025-10-10 22:26:27 -04:00
github-actions[bot]
d4d481e4b2 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/58402e09ad48f9f509d9249b8704c1a4f390c434?dir=pkgs/firefox-addons&narHash=sha256-vWk2iw/i5c0RHc/zLVyB51UYvmbzPq3uB14NN7kaouE%3D' (2025-09-26)
  → 'gitlab:rycee/nur-expressions/df243385cb4a2263b4888687961ec1f2b5f641f3?dir=pkgs/firefox-addons&narHash=sha256-wy15H4AbfQpTWes4aoFaIxkvK6e9p0lyvypOsTMt5f0%3D' (2025-10-03)
• Updated input 'home-manager':
    'github:nix-community/home-manager/bc2afee55bc5d3b825287829d6592b9cc1405aad?narHash=sha256-ZqaRdd%2BKoR54dNJPtd7UX4O0X%2B02YItnTpQVu28lSVI%3D' (2025-09-26)
  → 'github:nix-community/home-manager/edafd6da1936426708f1be0b1a4288007f16639a?narHash=sha256-0aXlKPxm2M%2BF5oywX2TTbY0e6h%2BtQ%2B6OYyx7UZn3A4A%3D' (2025-10-04)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/170ff93c860b2a9868ed1e1102d4e52cb3d934e1?narHash=sha256-6CFdj7Xs616t1W4jLDH7IohAAvl5Dyib3qEv/Uqw1rk%3D' (2025-09-23)
  → 'github:nixos/nixos-hardware/e087756cf4abbe1a34f3544c480fc1034d68742f?narHash=sha256-wPd5oGvBBpUEzMF0kWnXge0WITNsITx/aGI9qLHgJ4g%3D' (2025-09-30)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/e643668fd71b949c53f8626614b21ff71a07379d?narHash=sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o%3D' (2025-09-24)
  → 'github:nixos/nixpkgs/7df7ff7d8e00218376575f0acdcc5d66741351ee?narHash=sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs%3D' (2025-10-02)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/a62c2e5c3808f706316d1477ea3f8d02128886af?narHash=sha256-rjkIdtpJ47tJ3AmTnQLWKhm51xTI1GgGh/pVN3rBZzQ%3D' (2025-09-27)
  → 'github:nixos/nixpkgs/27ac93958969b5f3dccd654b402599cf3de633ac?narHash=sha256-hhM8SUI6kQMei5TImFdNQy9EDT8g2hAD161DUtbfAy0%3D' (2025-10-04)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/e0fdaea3c31646e252a60b42d0ed8eafdb289762?narHash=sha256-L3N8zV6wsViXiD8i3WFyrvjDdz76g3tXKEdZ4FkgQ%2BY%3D' (2025-09-21)
  → 'github:Mic92/sops-nix/9fcfabe085281dd793589bdc770a2e577a3caa5d?narHash=sha256-f9QC2KKiNReZDG2yyKAtDZh0rSK2Xp1wkPzKbHeQVRU%3D' (2025-09-29)
2025-10-03 22:24:39 -04:00
f092348736 updated elises groups 2025-09-27 12:36:33 -04:00
e6c3ae0bee adding xcursorgen for elise 2025-09-27 12:29:04 -04:00
d1f4f21521 added zerotier to leviathan 2025-09-27 11:56:20 -04:00
00a5536208 updated brain ssh config 2025-09-27 11:56:20 -04:00
b554325b13 added brain to syncthing 2025-09-27 11:56:20 -04:00
357168695c updated brain ip 2025-09-27 11:56:20 -04:00
fc31447591 updated brain ip 2025-09-27 11:56:20 -04:00
eea620aa2f Removed users.mutableUsers = True from brain 2025-09-27 11:56:20 -04:00
82d463bfd6 fixed imports 2025-09-27 11:56:20 -04:00
ca8c9925ad added brain to the flake 2025-09-27 11:56:20 -04:00
45a31e6b4d updated networking 2025-09-27 11:56:20 -04:00
c4be520190 adding brain 2025-09-27 11:56:20 -04:00
28cd7f3f6f adding user for dov 2025-09-27 11:56:20 -04:00
github-actions[bot]
879885dc3b flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/7c0e1d343108cbaaf448353fadb62190246251a8?dir=pkgs/firefox-addons&narHash=sha256-PsQSN226ZZ4KnweNspxKTzF8ztdPOAT6%2BgpGkxnygpg%3D' (2025-09-19)
  → 'gitlab:rycee/nur-expressions/58402e09ad48f9f509d9249b8704c1a4f390c434?dir=pkgs/firefox-addons&narHash=sha256-vWk2iw/i5c0RHc/zLVyB51UYvmbzPq3uB14NN7kaouE%3D' (2025-09-26)
• Updated input 'home-manager':
    'github:nix-community/home-manager/55b1f5b7b191572257545413b98e37abab2fdb00?narHash=sha256-l60D1i0aaSqemy9dL7wP0ePMfcv/oZbeKpvUMY%2Bq0kQ%3D' (2025-09-19)
  → 'github:nix-community/home-manager/bc2afee55bc5d3b825287829d6592b9cc1405aad?narHash=sha256-ZqaRdd%2BKoR54dNJPtd7UX4O0X%2B02YItnTpQVu28lSVI%3D' (2025-09-26)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/67a709cfe5d0643dafd798b0b613ed579de8be05?narHash=sha256-w6cDExPBqbq7fTLo4dZ1ozDGeq3yV6dSN4n/sAaS6OM%3D' (2025-09-15)
  → 'github:nixos/nixos-hardware/170ff93c860b2a9868ed1e1102d4e52cb3d934e1?narHash=sha256-6CFdj7Xs616t1W4jLDH7IohAAvl5Dyib3qEv/Uqw1rk%3D' (2025-09-23)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/0147c2f1d54b30b5dd6d4a8c8542e8d7edf93b5d?narHash=sha256-7To75JlpekfUmdkUZewnT6MoBANS0XVypW6kjUOXQwc%3D' (2025-09-18)
  → 'github:nixos/nixpkgs/e643668fd71b949c53f8626614b21ff71a07379d?narHash=sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o%3D' (2025-09-24)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/319145a716f6547ff9cce8ddb32568726b24427b?narHash=sha256-4s7UlIKlZS7zC5545quIPh6YHkO491RRyNC5G2nqinQ%3D' (2025-09-20)
  → 'github:nixos/nixpkgs/a62c2e5c3808f706316d1477ea3f8d02128886af?narHash=sha256-rjkIdtpJ47tJ3AmTnQLWKhm51xTI1GgGh/pVN3rBZzQ%3D' (2025-09-27)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/f77d4cfa075c3de66fc9976b80e0c4fc69e2c139?narHash=sha256-HYnwlbY6RE5xVd5rh0bYw77pnD8lOgbT4mlrfjgNZ0c%3D' (2025-09-16)
  → 'github:Mic92/sops-nix/e0fdaea3c31646e252a60b42d0ed8eafdb289762?narHash=sha256-L3N8zV6wsViXiD8i3WFyrvjDdz76g3tXKEdZ4FkgQ%2BY%3D' (2025-09-21)
• Updated input 'system_tools':
    'github:RichieCahill/system_tools/d63c486fe3b76c24b2ed2fff33d6f54c847b50e8?narHash=sha256-6r45DD/tMN%2BhYgnMc2/c82Z0bb1A7FnI/nvU8kZf/Us%3D' (2025-09-15)
  → 'github:RichieCahill/system_tools/b79f5abdf9237b9c07c075a6f96a88071ae5ac8b?narHash=sha256-TCuxPVk4Cq9MazXED0APSJi12tOIyT8MgSO5hGJU1l0%3D' (2025-09-21)
2025-09-26 22:26:23 -04:00
5e03efa1e8 removed password 2025-09-26 22:03:12 -04:00
2ed08501fe adding user for dov 2025-09-26 22:03:12 -04:00
c36624cc55 added compression to jeeves ssh settings 2025-09-24 18:16:49 -04:00
f074344ac8 feature/updated Elises gui packages 2025-09-24 18:16:49 -04:00
github-actions[bot]
b773eb2db7 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/285da329ed3e03565cba6f92b7f21350831266e3?dir=pkgs/firefox-addons&narHash=sha256-Fsb3Tpo3QRlRAKvDCoMvWD99y%2BioY62kgU/cjMr203U%3D' (2025-09-18)
  → 'gitlab:rycee/nur-expressions/7c0e1d343108cbaaf448353fadb62190246251a8?dir=pkgs/firefox-addons&narHash=sha256-PsQSN226ZZ4KnweNspxKTzF8ztdPOAT6%2BgpGkxnygpg%3D' (2025-09-19)
• Updated input 'home-manager':
    'github:nix-community/home-manager/b5698ed57db7ee7da5e93df2e6bbada91c88f3ce?narHash=sha256-BG7GlXo5moXtrFSCqnkIb1Q00szOZXTj5Dx7NmWgves%3D' (2025-09-18)
  → 'github:nix-community/home-manager/55b1f5b7b191572257545413b98e37abab2fdb00?narHash=sha256-l60D1i0aaSqemy9dL7wP0ePMfcv/oZbeKpvUMY%2Bq0kQ%3D' (2025-09-19)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/8d4ddb19d03c65a36ad8d189d001dc32ffb0306b?narHash=sha256-qqIJ3yxPiB0ZQTT9//nFGQYn8X/PBoJbofA7hRKZnmE%3D' (2025-09-16)
  → 'github:nixos/nixpkgs/0147c2f1d54b30b5dd6d4a8c8542e8d7edf93b5d?narHash=sha256-7To75JlpekfUmdkUZewnT6MoBANS0XVypW6kjUOXQwc%3D' (2025-09-18)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/2ea7a78d192f539e8e70367c6de01e03fdd6f072?narHash=sha256-gzHfMSq3zKUdTfTZ7WMptrxS8yGwCC5wlXeK6OU9AXU%3D' (2025-09-18)
  → 'github:nixos/nixpkgs/319145a716f6547ff9cce8ddb32568726b24427b?narHash=sha256-4s7UlIKlZS7zC5545quIPh6YHkO491RRyNC5G2nqinQ%3D' (2025-09-20)
2025-09-19 22:26:47 -04:00
github-actions[bot]
6efcc9add1 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/789920825fc982a93a2bf91a714367fa8f7ea0a6?dir=pkgs/firefox-addons&narHash=sha256-VjtA%2BfqkraKHbGzjKJBPfDj%2BSXysXiR4SrghTr10HoY%3D' (2025-09-12)
  → 'gitlab:rycee/nur-expressions/285da329ed3e03565cba6f92b7f21350831266e3?dir=pkgs/firefox-addons&narHash=sha256-Fsb3Tpo3QRlRAKvDCoMvWD99y%2BioY62kgU/cjMr203U%3D' (2025-09-18)
• Updated input 'home-manager':
    'github:nix-community/home-manager/a3fcc92180c7462082cd849498369591dfb20855?narHash=sha256-UqHHGydF/q3jfYXCpvYLA0TWtvByOp1NwOKCUjhYmPs%3D' (2025-09-12)
  → 'github:nix-community/home-manager/b5698ed57db7ee7da5e93df2e6bbada91c88f3ce?narHash=sha256-BG7GlXo5moXtrFSCqnkIb1Q00szOZXTj5Dx7NmWgves%3D' (2025-09-18)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/11b2a10c7be726321bb854403fdeec391e798bf0?narHash=sha256-PtT7ix43ss8PONJ1VJw3f6t2yAoGH%2Bq462Sn8lrmWmk%3D' (2025-09-05)
  → 'github:nixos/nixos-hardware/67a709cfe5d0643dafd798b0b613ed579de8be05?narHash=sha256-w6cDExPBqbq7fTLo4dZ1ozDGeq3yV6dSN4n/sAaS6OM%3D' (2025-09-15)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/ab0f3607a6c7486ea22229b92ed2d355f1482ee0?narHash=sha256-zwE/e7CuPJUWKdvvTCB7iunV4E/%2BG0lKfv4kk/5Izdg%3D' (2025-09-10)
  → 'github:nixos/nixpkgs/8d4ddb19d03c65a36ad8d189d001dc32ffb0306b?narHash=sha256-qqIJ3yxPiB0ZQTT9//nFGQYn8X/PBoJbofA7hRKZnmE%3D' (2025-09-16)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/2ca437b4796d049192eb30576a50fef139038c09?narHash=sha256-VBS5%2BYKIT8Aj81ZW%2B8Bg9MuYoI6OqO6HSrwG4dpHpW4%3D' (2025-09-12)
  → 'github:nixos/nixpkgs/2ea7a78d192f539e8e70367c6de01e03fdd6f072?narHash=sha256-gzHfMSq3zKUdTfTZ7WMptrxS8yGwCC5wlXeK6OU9AXU%3D' (2025-09-18)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/0bf793823386187dff101ee2a9d4ed26de8bbf8c?narHash=sha256-S9F6bHUBh%2BCFEUalv/qxNImRapCxvSnOzWBUZgK1zDU%3D' (2025-09-10)
  → 'github:Mic92/sops-nix/f77d4cfa075c3de66fc9976b80e0c4fc69e2c139?narHash=sha256-HYnwlbY6RE5xVd5rh0bYw77pnD8lOgbT4mlrfjgNZ0c%3D' (2025-09-16)
2025-09-18 21:28:20 -04:00
bff561946f reviewed coderabbit 2025-09-15 08:55:55 -04:00
1ec05d13da updated system_tools 2025-09-15 08:55:55 -04:00
4686a85bcd added EnvironmentFile to snapshot_manager 2025-09-15 08:55:55 -04:00
3434b32fbe added signal_cli_rest_api 2025-09-15 08:55:55 -04:00
98d9efcde3 updated system_tools 2025-09-15 08:55:55 -04:00
github-actions[bot]
4286f39177 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/1b0f57c7a959cdfe8e68b98845ae8445cb0aa132?dir=pkgs/firefox-addons&narHash=sha256-K3%2BOooIM2hf8fauVMMY79C%2BCHHxbUkOyWfz8YpNxZcM%3D' (2025-09-05)
  → 'gitlab:rycee/nur-expressions/789920825fc982a93a2bf91a714367fa8f7ea0a6?dir=pkgs/firefox-addons&narHash=sha256-VjtA%2BfqkraKHbGzjKJBPfDj%2BSXysXiR4SrghTr10HoY%3D' (2025-09-12)
• Updated input 'home-manager':
    'github:nix-community/home-manager/f56bf065f9abedc7bc15e1f2454aa5c8edabaacf?narHash=sha256-a%2BNMGl5tcvm%2BhyfSG2DlVPa8nZLpsumuRj1FfcKb2mQ%3D' (2025-09-05)
  → 'github:nix-community/home-manager/a3fcc92180c7462082cd849498369591dfb20855?narHash=sha256-UqHHGydF/q3jfYXCpvYLA0TWtvByOp1NwOKCUjhYmPs%3D' (2025-09-12)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/d0fc30899600b9b3466ddb260fd83deb486c32f1?narHash=sha256-rw/PHa1cqiePdBxhF66V7R%2BWAP8WekQ0mCDG4CFqT8Y%3D' (2025-09-02)
  → 'github:nixos/nixpkgs/ab0f3607a6c7486ea22229b92ed2d355f1482ee0?narHash=sha256-zwE/e7CuPJUWKdvvTCB7iunV4E/%2BG0lKfv4kk/5Izdg%3D' (2025-09-10)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/d1537033b5e932eeb7c89165ead8c0f3b322ff31?narHash=sha256-%2BdcNz3EOCmZd2XCxeTcucoGNK8Onn4518yqhCr4x0w8%3D' (2025-09-06)
  → 'github:nixos/nixpkgs/2ca437b4796d049192eb30576a50fef139038c09?narHash=sha256-VBS5%2BYKIT8Aj81ZW%2B8Bg9MuYoI6OqO6HSrwG4dpHpW4%3D' (2025-09-12)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/3223c7a92724b5d804e9988c6b447a0d09017d48?narHash=sha256-t%2Bvoe2961vCgrzPFtZxha0/kmFSHFobzF00sT8p9h0U%3D' (2025-08-12)
  → 'github:Mic92/sops-nix/0bf793823386187dff101ee2a9d4ed26de8bbf8c?narHash=sha256-S9F6bHUBh%2BCFEUalv/qxNImRapCxvSnOzWBUZgK1zDU%3D' (2025-09-10)
2025-09-12 22:22:47 -04:00
0fe439ceaf updated HostName in programs.ssh.extraConfig 2025-09-12 00:15:54 -04:00
7a3c2026b3 updating programs ssh settings 2025-09-12 00:12:18 -04:00
0c9ce78c20 added testdisk 2025-09-10 22:06:54 -04:00
c10a76babc removed games.nix 2025-09-10 21:58:26 -04:00
113ca9c99a removed open-webui 2025-09-10 21:58:26 -04:00
29f51bf116 testing linuxPackages_6_16 2025-09-10 21:58:26 -04:00
9f3a2b2a4b testing continue-on-error: true and linuxPackages_6_17 2025-09-10 21:58:26 -04:00
github-actions[bot]
8a4d021541 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/56254103ed36317cf471da3c93ac13805c72a7fa?dir=pkgs/firefox-addons&narHash=sha256-RzTBJ5ERXrJk7JPIDp1vjUNMKka6tyg4RctIR3cxqsM%3D' (2025-08-22)
  → 'gitlab:rycee/nur-expressions/1b0f57c7a959cdfe8e68b98845ae8445cb0aa132?dir=pkgs/firefox-addons&narHash=sha256-K3%2BOooIM2hf8fauVMMY79C%2BCHHxbUkOyWfz8YpNxZcM%3D' (2025-09-05)
• Updated input 'home-manager':
    'github:nix-community/home-manager/6911d3e7f475f7b3558b4f5a6aba90fa86099baa?narHash=sha256-QdenO8f0PTg%2BtC6HuSvngKcbRZA5oZKmjUT%2BMXKOLQg%3D' (2025-08-21)
  → 'github:nix-community/home-manager/f56bf065f9abedc7bc15e1f2454aa5c8edabaacf?narHash=sha256-a%2BNMGl5tcvm%2BhyfSG2DlVPa8nZLpsumuRj1FfcKb2mQ%3D' (2025-09-05)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/3dac8a872557e0ca8c083cdcfc2f218d18e113b0?narHash=sha256-aJHFJWP9AuI8jUGzI77LYcSlkA9wJnOIg4ZqftwNGXA%3D' (2025-08-16)
  → 'github:nixos/nixos-hardware/11b2a10c7be726321bb854403fdeec391e798bf0?narHash=sha256-PtT7ix43ss8PONJ1VJw3f6t2yAoGH%2Bq462Sn8lrmWmk%3D' (2025-09-05)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/20075955deac2583bb12f07151c2df830ef346b4?narHash=sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs%2BStOp19xNsbqdOg%3D' (2025-08-19)
  → 'github:nixos/nixpkgs/d0fc30899600b9b3466ddb260fd83deb486c32f1?narHash=sha256-rw/PHa1cqiePdBxhF66V7R%2BWAP8WekQ0mCDG4CFqT8Y%3D' (2025-09-02)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/db18278efcff292f5c3f41c52fc89f5d69227155?narHash=sha256-eK6b2F%2BQlBPQjrkhuf/1msR5Z2guESncBw%2BfuwNPVOA%3D' (2025-08-22)
  → 'github:nixos/nixpkgs/d1537033b5e932eeb7c89165ead8c0f3b322ff31?narHash=sha256-%2BdcNz3EOCmZd2XCxeTcucoGNK8Onn4518yqhCr4x0w8%3D' (2025-09-06)
2025-09-10 21:58:26 -04:00
5225bf1732 removed gpt-oss:120b 2025-09-02 21:34:06 -04:00
7a5dea1c36 updated vscode setting dir 2025-09-01 14:10:43 -04:00
0956ea6f58 updated git users 2025-09-01 14:10:43 -04:00
5d643e0cd1 updated secrets.yaml 2025-09-01 14:10:43 -04:00
ed3805a89e added notes to sops.yaml 2025-09-01 14:10:43 -04:00
ce8de13734 added leviathan to sops.yaml 2025-09-01 14:10:43 -04:00
68b7a2f80d added leviathan to flake.nix 2025-09-01 14:10:43 -04:00
62c3aa69fe added leviathan and elise 2025-09-01 14:10:43 -04:00
470be2b761 updated llms to local host only 2025-09-01 13:01:46 -04:00
e1c5bd0f84 updated loadModels and colsed open-webui firewall 2025-09-01 13:01:46 -04:00
786b275f7c adding olama group to richie 2025-09-01 13:01:46 -04:00
324721ff8d adding zerotier network to jeeves 2025-09-01 13:01:46 -04:00
7137435703 updating jeeves llm setting 2025-09-01 13:01:46 -04:00
a7b336a7de adding llms to rhapsody-in-green 2025-09-01 13:01:46 -04:00
529b03525b added kafka and ollama data sets 2025-08-23 10:43:56 -04:00
d0364bdaad fixed typo 2025-08-23 10:43:56 -04:00
45ddf8bc54 adding kafka and ollama 2025-08-23 10:43:56 -04:00
github-actions[bot]
6913c7046e flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/e6c2e889b34f5f623a7749a46e2aa5ea6e7256a0?dir=pkgs/firefox-addons&narHash=sha256-KVPjWo/RVQBQe6N03cNbSVM/xNCv2506wE4A8wL73sk%3D' (2025-08-15)
  → 'gitlab:rycee/nur-expressions/56254103ed36317cf471da3c93ac13805c72a7fa?dir=pkgs/firefox-addons&narHash=sha256-RzTBJ5ERXrJk7JPIDp1vjUNMKka6tyg4RctIR3cxqsM%3D' (2025-08-22)
• Updated input 'home-manager':
    'github:nix-community/home-manager/11626a4383b458f8dc5ea3237eaa04e8ab1912f3?narHash=sha256-soZegto0xXzG2zYlu/zjknDHv0Z7tRS5EQs%2BZ/VRTBg%3D' (2025-08-15)
  → 'github:nix-community/home-manager/6911d3e7f475f7b3558b4f5a6aba90fa86099baa?narHash=sha256-QdenO8f0PTg%2BtC6HuSvngKcbRZA5oZKmjUT%2BMXKOLQg%3D' (2025-08-21)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/26ed7a0d4b8741fe1ef1ee6fa64453ca056ce113?narHash=sha256-dz303vGuzWjzOPOaYkS9xSW%2BB93PSAJxvBd6CambXVA%3D' (2025-08-07)
  → 'github:nixos/nixos-hardware/3dac8a872557e0ca8c083cdcfc2f218d18e113b0?narHash=sha256-aJHFJWP9AuI8jUGzI77LYcSlkA9wJnOIg4ZqftwNGXA%3D' (2025-08-16)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/fbcf476f790d8a217c3eab4e12033dc4a0f6d23c?narHash=sha256-wNO3%2BKs2jZJ4nTHMuks%2BcxAiVBGNuEBXsT29Bz6HASo%3D' (2025-08-14)
  → 'github:nixos/nixpkgs/20075955deac2583bb12f07151c2df830ef346b4?narHash=sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs%2BStOp19xNsbqdOg%3D' (2025-08-19)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/25bb95af3131646b807ffc4421c4c4a40dfa89c8?narHash=sha256-lNEV%2Bi4epKQTbEduUb5rsxf36onD6KTdzMB5WnEECiU%3D' (2025-08-16)
  → 'github:nixos/nixpkgs/db18278efcff292f5c3f41c52fc89f5d69227155?narHash=sha256-eK6b2F%2BQlBPQjrkhuf/1msR5Z2guESncBw%2BfuwNPVOA%3D' (2025-08-22)
2025-08-22 22:30:34 -04:00
c4d2b0b8d4 added lib.mkDefault tpo global environment 2025-08-17 10:35:37 -04:00
27e939459b updated postgresql settings 2025-08-17 10:35:37 -04:00
501a838be9 updated matts user 2025-08-17 10:35:37 -04:00
2325249687 added c library's for numpy 2025-08-17 10:35:37 -04:00
github-actions[bot]
45951f6525 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/b657cfddb78408e9b53b4a8aaeaac71fc7ea182e?dir=pkgs/firefox-addons&narHash=sha256-FE908x/ihUlr5yn1f%2BPTMyOjcwotGUodzn7Ej6zZf5U%3D' (2025-07-29)
  → 'gitlab:rycee/nur-expressions/e6c2e889b34f5f623a7749a46e2aa5ea6e7256a0?dir=pkgs/firefox-addons&narHash=sha256-KVPjWo/RVQBQe6N03cNbSVM/xNCv2506wE4A8wL73sk%3D' (2025-08-15)
• Updated input 'home-manager':
    'github:nix-community/home-manager/e102920c1becb114645c6f92fe14edc0b05cc229?narHash=sha256-kVHCrTWEe8B1thAhFag1bk4QPY0ZP45V9vPbrwPHoNo%3D' (2025-08-01)
  → 'github:nix-community/home-manager/11626a4383b458f8dc5ea3237eaa04e8ab1912f3?narHash=sha256-soZegto0xXzG2zYlu/zjknDHv0Z7tRS5EQs%2BZ/VRTBg%3D' (2025-08-15)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/cc66fddc6cb04ab479a1bb062f4d4da27c936a22?narHash=sha256-nFxE8lk9JvGelxClCmwuJYftbHqwnc01dRN4DVLUroM%3D' (2025-07-21)
  → 'github:nixos/nixos-hardware/26ed7a0d4b8741fe1ef1ee6fa64453ca056ce113?narHash=sha256-dz303vGuzWjzOPOaYkS9xSW%2BB93PSAJxvBd6CambXVA%3D' (2025-08-07)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/94def634a20494ee057c76998843c015909d6311?narHash=sha256-K2ViRJfdVGE8tpJejs8Qpvvejks1%2BA4GQej/lBk5y7I%3D' (2025-07-31)
  → 'github:nixos/nixpkgs/fbcf476f790d8a217c3eab4e12033dc4a0f6d23c?narHash=sha256-wNO3%2BKs2jZJ4nTHMuks%2BcxAiVBGNuEBXsT29Bz6HASo%3D' (2025-08-14)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/3683d033f7994c92834f45f166275738dcdd959d?narHash=sha256-bklCdnAaAerEacvmy%2BHkltNR9QE/M4Ygy%2Bm0L2C6LJI%3D' (2025-08-01)
  → 'github:nixos/nixpkgs/25bb95af3131646b807ffc4421c4c4a40dfa89c8?narHash=sha256-lNEV%2Bi4epKQTbEduUb5rsxf36onD6KTdzMB5WnEECiU%3D' (2025-08-16)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/2c8def626f54708a9c38a5861866660395bb3461?narHash=sha256-GllP7cmQu7zLZTs9z0J2gIL42IZHa9CBEXwBY9szT0U%3D' (2025-07-15)
  → 'github:Mic92/sops-nix/3223c7a92724b5d804e9988c6b447a0d09017d48?narHash=sha256-t%2Bvoe2961vCgrzPFtZxha0/kmFSHFobzF00sT8p9h0U%3D' (2025-08-12)
2025-08-15 22:33:26 -04:00
fd56fa66f0 updated authorizedKeys and added math_password 2025-08-06 21:51:09 -04:00
1314298c0b treefmt 2025-08-06 21:43:19 -04:00
f0eee80c2d adding math user 2025-08-06 21:43:19 -04:00
c20b6d1da2 adding nodejs 2025-08-06 21:43:19 -04:00
34a59f966d testing nix-serve-ng 2025-08-01 22:38:47 -04:00
a677046330 updated flake lock 2025-08-01 20:44:30 -04:00
111afa1c6b enabling datagrip 2025-08-01 20:44:30 -04:00
c5f2805e74 ran treefmt 2025-08-01 20:41:59 -04:00
9a61d06f08 added encryptionPasswordFile to david server sync thing connection 2025-08-01 20:41:59 -04:00
559ca7a45e adding update the jeeves 2025-07-28 12:47:17 -04:00
03b636eb3a disabled jetbrains.datagrip 2025-07-28 12:41:26 -04:00
a7f5d3c71d removed ladybird 2025-07-28 12:41:26 -04:00
github-actions[bot]
3bbf8dc7a6 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/369dccacc0a8214600773b3bbbf68d98a19b74e8?dir=pkgs/firefox-addons&narHash=sha256-TVWYBcNpeQ3fEloryVP9N2yJnC3vuPpoZF1RqFFWFt0%3D' (2025-07-18)
  → 'gitlab:rycee/nur-expressions/553afee4efb5a7dea03cf654deafacd8fa1004f9?dir=pkgs/firefox-addons&narHash=sha256-45s1L4h/6t3M%2B/ppqow1OFUgfk9jZHsR4jxNgxIWWmM%3D' (2025-07-25)
• Updated input 'home-manager':
    'github:nix-community/home-manager/d0300c8808e41da81d6edfc202f3d3833c157daf?narHash=sha256-irfg7lnfEpJY%2B3Cffkluzp2MTVw1Uq9QGxFp6qadcXI%3D' (2025-07-18)
  → 'github:nix-community/home-manager/a1817d1c0e5eabe7dfdfe4caa46c94d9d8f3fdb6?narHash=sha256-hOUWU5L62G9sm8NxdiLWlLIJZz9H52VuFiDllHdwmVA%3D' (2025-07-25)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/d1bfa8f6ccfb5c383e1eba609c1eb67ca24ed153?narHash=sha256-P8J72psdc/rWliIvp8jUpoQ6qRDlVzgSDDlgkaXQ0Fw%3D' (2025-07-16)
  → 'github:nixos/nixos-hardware/cc66fddc6cb04ab479a1bb062f4d4da27c936a22?narHash=sha256-nFxE8lk9JvGelxClCmwuJYftbHqwnc01dRN4DVLUroM%3D' (2025-07-21)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/6e987485eb2c77e5dcc5af4e3c70843711ef9251?narHash=sha256-RKwfXA4OZROjBTQAl9WOZQFm7L8Bo93FQwSJpAiSRvo%3D' (2025-07-16)
  → 'github:nixos/nixpkgs/fc02ee70efb805d3b2865908a13ddd4474557ecf?narHash=sha256-i%2BCQV2rPmP8wHxj0aq4siYyohHwVlsh40kV89f3nw1s%3D' (2025-07-23)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/cda03d762aa4d6930f41c2a60b946e0f8a743630?narHash=sha256-ppPL0jXpeeFtyP9I01o807u3iVADQ3YkqpN2lUZk4LY%3D' (2025-07-19)
  → 'github:nixos/nixpkgs/ce6b0611ca70423d97fab2a3f53f61f1fa4ff0a4?narHash=sha256-Iy/9c6DaxCY9ECCLgpoo%2BuwY1K5YTmJLU7fSg7JeALk%3D' (2025-07-26)
2025-07-25 22:37:36 -04:00
github-actions[bot]
1cd4084ec8 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/9885400dbd82f9b2970b30e18f233404416f7cca?dir=pkgs/firefox-addons&narHash=sha256-vL26J2f9uXvwBNkfwYH1v75VwN22ZLhBcyZeenJwnCU%3D' (2025-07-11)
  → 'gitlab:rycee/nur-expressions/369dccacc0a8214600773b3bbbf68d98a19b74e8?dir=pkgs/firefox-addons&narHash=sha256-TVWYBcNpeQ3fEloryVP9N2yJnC3vuPpoZF1RqFFWFt0%3D' (2025-07-18)
• Updated input 'home-manager':
    'github:nix-community/home-manager/3976e0507edc9a5f332cb2be93fa20e646d22374?narHash=sha256-YhnBM3oknReSFTAuc2SMwekwjl9nDd5PUhcar4DsefM%3D' (2025-07-11)
  → 'github:nix-community/home-manager/d0300c8808e41da81d6edfc202f3d3833c157daf?narHash=sha256-irfg7lnfEpJY%2B3Cffkluzp2MTVw1Uq9QGxFp6qadcXI%3D' (2025-07-18)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/7ced9122cff2163c6a0212b8d1ec8c33a1660806?narHash=sha256-gATnkOe37eeVwKKYCsL%2BOnS2gU4MmLuZFzzWCtaKLI8%3D' (2025-07-09)
  → 'github:nixos/nixos-hardware/d1bfa8f6ccfb5c383e1eba609c1eb67ca24ed153?narHash=sha256-P8J72psdc/rWliIvp8jUpoQ6qRDlVzgSDDlgkaXQ0Fw%3D' (2025-07-16)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/9807714d6944a957c2e036f84b0ff8caf9930bc0?narHash=sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X%2BxgOL0%3D' (2025-07-08)
  → 'github:nixos/nixpkgs/6e987485eb2c77e5dcc5af4e3c70843711ef9251?narHash=sha256-RKwfXA4OZROjBTQAl9WOZQFm7L8Bo93FQwSJpAiSRvo%3D' (2025-07-16)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/bcef7b39781011d4332b54f59e989d4d4c22d643?narHash=sha256-yTQSi9WAqiGKLbCj3aO96QBp5F9u6hmL454uiKDCDrU%3D' (2025-07-11)
  → 'github:nixos/nixpkgs/cda03d762aa4d6930f41c2a60b946e0f8a743630?narHash=sha256-ppPL0jXpeeFtyP9I01o807u3iVADQ3YkqpN2lUZk4LY%3D' (2025-07-19)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/3633fc4acf03f43b260244d94c71e9e14a2f6e0d?narHash=sha256-KrDPXobG7DFKTOteqdSVeL1bMVitDcy7otpVZWDE6MA%3D' (2025-07-04)
  → 'github:Mic92/sops-nix/2c8def626f54708a9c38a5861866660395bb3461?narHash=sha256-GllP7cmQu7zLZTs9z0J2gIL42IZHa9CBEXwBY9szT0U%3D' (2025-07-15)
2025-07-18 22:36:18 -04:00
github-actions[bot]
e65b4b696a flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/0c5b037915083a27a260b063b127b31443827bae?dir=pkgs/firefox-addons&narHash=sha256-bAKNekZcZd4QnBO/RUxjQAgaz67bYwFXWfQENA45Scg%3D' (2025-07-04)
  → 'gitlab:rycee/nur-expressions/9885400dbd82f9b2970b30e18f233404416f7cca?dir=pkgs/firefox-addons&narHash=sha256-vL26J2f9uXvwBNkfwYH1v75VwN22ZLhBcyZeenJwnCU%3D' (2025-07-11)
• Updated input 'home-manager':
    'github:nix-community/home-manager/7d9e3c35f0d46f82bac791d76260f15f53d83529?narHash=sha256-7HiC6w4ROEbMmKtj5pilnLOJej9HkkfU9wEd5QSTyNo%3D' (2025-07-04)
  → 'github:nix-community/home-manager/3976e0507edc9a5f332cb2be93fa20e646d22374?narHash=sha256-YhnBM3oknReSFTAuc2SMwekwjl9nDd5PUhcar4DsefM%3D' (2025-07-11)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/497ae1357f1ac97f1aea31a4cb74ad0d534ef41f?narHash=sha256-136MeWtckSHTN9Z2WRNRdZ8oRP3vyx3L8UxeBYE%2BJ9w%3D' (2025-07-02)
  → 'github:nixos/nixos-hardware/7ced9122cff2163c6a0212b8d1ec8c33a1660806?narHash=sha256-gATnkOe37eeVwKKYCsL%2BOnS2gU4MmLuZFzzWCtaKLI8%3D' (2025-07-09)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/3016b4b15d13f3089db8a41ef937b13a9e33a8df?narHash=sha256-P/SQmKDu06x8yv7i0s8bvnnuJYkxVGBWLWHaU%2Btt4YY%3D' (2025-06-30)
  → 'github:nixos/nixpkgs/9807714d6944a957c2e036f84b0ff8caf9930bc0?narHash=sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X%2BxgOL0%3D' (2025-07-08)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/b441e40fcd70ff4368df31a7d5dfcc79e3596504?narHash=sha256-XnlCDd58iYO%2BaUBQIyxFOfkoFRDCq9ajaZFaaGwHc3Y%3D' (2025-07-05)
  → 'github:nixos/nixpkgs/bcef7b39781011d4332b54f59e989d4d4c22d643?narHash=sha256-yTQSi9WAqiGKLbCj3aO96QBp5F9u6hmL454uiKDCDrU%3D' (2025-07-11)
2025-07-11 22:39:14 -04:00
github-actions[bot]
20a4a8c2fc flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/8c101ae6d55115c7d4f12bffa932f30416dfd070?dir=pkgs/firefox-addons&narHash=sha256-kGbUMc4hww2Fe6DdCFy8Qnlspc4dLepa1L9SLh0LORg%3D' (2025-06-27)
  → 'gitlab:rycee/nur-expressions/0c5b037915083a27a260b063b127b31443827bae?dir=pkgs/firefox-addons&narHash=sha256-bAKNekZcZd4QnBO/RUxjQAgaz67bYwFXWfQENA45Scg%3D' (2025-07-04)
• Updated input 'home-manager':
    'github:nix-community/home-manager/080e8b48b0318b38143d5865de9334f46d51fce3?narHash=sha256-BZXgag7I0rnL/HMHAsBz3tQrfKAibpY2vovexl2lS%2BY%3D' (2025-06-26)
  → 'github:nix-community/home-manager/7d9e3c35f0d46f82bac791d76260f15f53d83529?narHash=sha256-7HiC6w4ROEbMmKtj5pilnLOJej9HkkfU9wEd5QSTyNo%3D' (2025-07-04)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/98236410ea0fe204d0447149537a924fb71a6d4f?narHash=sha256-2m1ceZjbmgrJCZ2PuQZaK4in3gcg3o6rZ7WK6dr5vAA%3D' (2025-06-25)
  → 'github:nixos/nixos-hardware/497ae1357f1ac97f1aea31a4cb74ad0d534ef41f?narHash=sha256-136MeWtckSHTN9Z2WRNRdZ8oRP3vyx3L8UxeBYE%2BJ9w%3D' (2025-07-02)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/30a61f056ac492e3b7cdcb69c1e6abdcf00e39cf?narHash=sha256-/CG%2Bw0o0oJ5itVklOoLbdn2dGB0wbZVOoDm4np6w09A%3D' (2025-06-24)
  → 'github:nixos/nixpkgs/3016b4b15d13f3089db8a41ef937b13a9e33a8df?narHash=sha256-P/SQmKDu06x8yv7i0s8bvnnuJYkxVGBWLWHaU%2Btt4YY%3D' (2025-06-30)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/c0874d923d0fd19d73d95a02820acd27ae11e320?narHash=sha256-FQQyEt6/Q3FNeCkY9Gu1JeKBLzRQnZL%2BWYkjc53shLM%3D' (2025-06-27)
  → 'github:nixos/nixpkgs/b441e40fcd70ff4368df31a7d5dfcc79e3596504?narHash=sha256-XnlCDd58iYO%2BaUBQIyxFOfkoFRDCq9ajaZFaaGwHc3Y%3D' (2025-07-05)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/77c423a03b9b2b79709ea2cb63336312e78b72e2?narHash=sha256-Rr7Pooz9zQbhdVxux16h7URa6mA80Pb/G07T4lHvh0M%3D' (2025-06-17)
  → 'github:Mic92/sops-nix/3633fc4acf03f43b260244d94c71e9e14a2f6e0d?narHash=sha256-KrDPXobG7DFKTOteqdSVeL1bMVitDcy7otpVZWDE6MA%3D' (2025-07-04)
2025-07-04 22:33:05 -04:00
f4348c2ab5 update desktop.nix kernelPackages to linuxPackages_6_15 2025-07-02 08:21:41 -04:00
6eab8497ba adding uv to my programs 2025-06-30 20:13:06 -04:00
github-actions[bot]
da5cdb8f05 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/26d54102450b4e23d56d4319ef0722f155b5b37c?dir=pkgs/firefox-addons&narHash=sha256-W%2BPwT19YbzRH8YawEeV3jBb5dp8CbfwWZ4uFQkGwZbA%3D' (2025-06-21)
  → 'gitlab:rycee/nur-expressions/8c101ae6d55115c7d4f12bffa932f30416dfd070?dir=pkgs/firefox-addons&narHash=sha256-kGbUMc4hww2Fe6DdCFy8Qnlspc4dLepa1L9SLh0LORg%3D' (2025-06-27)
• Updated input 'home-manager':
    'github:nix-community/home-manager/863842639722dd12ae9e37ca83bcb61a63b36f6c?narHash=sha256-Mj5t4yX05/rXnRqJkpoLZTWqgStB88Mr/fegTRqyiWc%3D' (2025-06-19)
  → 'github:nix-community/home-manager/080e8b48b0318b38143d5865de9334f46d51fce3?narHash=sha256-BZXgag7I0rnL/HMHAsBz3tQrfKAibpY2vovexl2lS%2BY%3D' (2025-06-26)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/1552a9f4513f3f0ceedcf90320e48d3d47165712?narHash=sha256-vnzzBDbCGvInmfn2ijC4HsIY/3W1CWbwS/YQoFgdgPg%3D' (2025-06-20)
  → 'github:nixos/nixos-hardware/98236410ea0fe204d0447149537a924fb71a6d4f?narHash=sha256-2m1ceZjbmgrJCZ2PuQZaK4in3gcg3o6rZ7WK6dr5vAA%3D' (2025-06-25)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/08f22084e6085d19bcfb4be30d1ca76ecb96fe54?narHash=sha256-XE/lFNhz5lsriMm/yjXkvSZz5DfvKJLUjsS6pP8EC50%3D' (2025-06-19)
  → 'github:nixos/nixpkgs/30a61f056ac492e3b7cdcb69c1e6abdcf00e39cf?narHash=sha256-/CG%2Bw0o0oJ5itVklOoLbdn2dGB0wbZVOoDm4np6w09A%3D' (2025-06-24)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/b5b8d1f94bd353640131a6a38d2b01d0bfc13012?narHash=sha256-NT8CNfGLNBI%2BuwjNC8HnHv0O6K1P1Nb%2B/g2eg%2Bu0gnE%3D' (2025-06-22)
  → 'github:nixos/nixpkgs/c0874d923d0fd19d73d95a02820acd27ae11e320?narHash=sha256-FQQyEt6/Q3FNeCkY9Gu1JeKBLzRQnZL%2BWYkjc53shLM%3D' (2025-06-27)
2025-06-27 22:33:15 -04:00
8aa9eea322 testing --rebase 2025-06-22 21:33:21 -04:00
febe7c7e53 test 2025-06-22 21:33:21 -04:00
b1ca58b2f4 adding Checkout repository 2025-06-22 21:33:21 -04:00
github-actions[bot]
7ad4ccd5ca flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/a00ce73b626ed274fbfe9f51627861e140b08f6d?dir=pkgs/firefox-addons&narHash=sha256-b3y7V7db0VwLGtpcLRmT1Aa9dpAKoHQdem55UhgB/fw%3D' (2025-06-18)
  → 'gitlab:rycee/nur-expressions/26d54102450b4e23d56d4319ef0722f155b5b37c?dir=pkgs/firefox-addons&narHash=sha256-W%2BPwT19YbzRH8YawEeV3jBb5dp8CbfwWZ4uFQkGwZbA%3D' (2025-06-21)
• Updated input 'home-manager':
    'github:nix-community/home-manager/0f355844e54e4c70906b1ef5cc35a0047d666c04?narHash=sha256-upC/GIlsIgtdtWRGd1obzdXWYQptNkfzZeyAFWgsgf0%3D' (2025-06-18)
  → 'github:nix-community/home-manager/863842639722dd12ae9e37ca83bcb61a63b36f6c?narHash=sha256-Mj5t4yX05/rXnRqJkpoLZTWqgStB88Mr/fegTRqyiWc%3D' (2025-06-19)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/61837d2a33ccc1582c5fabb7bf9130d39fee59ad?narHash=sha256-ynqbgIYrg7P1fAKYqe8I/PMiLABBcNDYG9YaAP/d/C4%3D' (2025-06-16)
  → 'github:nixos/nixos-hardware/1552a9f4513f3f0ceedcf90320e48d3d47165712?narHash=sha256-vnzzBDbCGvInmfn2ijC4HsIY/3W1CWbwS/YQoFgdgPg%3D' (2025-06-20)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/9e83b64f727c88a7711a2c463a7b16eedb69a84c?narHash=sha256-v263g4GbxXv87hMXMCpjkIxd/viIF7p3JpJrwgKdNiI%3D' (2025-06-17)
  → 'github:nixos/nixpkgs/08f22084e6085d19bcfb4be30d1ca76ecb96fe54?narHash=sha256-XE/lFNhz5lsriMm/yjXkvSZz5DfvKJLUjsS6pP8EC50%3D' (2025-06-19)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/ba92ab5dc0759a8740003ca34b5c1b888f4766d4?narHash=sha256-JW40%2BzIiDS%2BrZavb9IYdIN40/GmErO2%2B0%2BA66rM6/b8%3D' (2025-06-19)
  → 'github:nixos/nixpkgs/b5b8d1f94bd353640131a6a38d2b01d0bfc13012?narHash=sha256-NT8CNfGLNBI%2BuwjNC8HnHv0O6K1P1Nb%2B/g2eg%2Bu0gnE%3D' (2025-06-22)
2025-06-22 21:11:00 -04:00
c936501afb bugfix fixing problems in merge_flake_lock_update 2025-06-22 20:48:10 -04:00
239d7833f6 small update 2025-06-22 20:44:28 -04:00
8fb6ae41b9 added gh pr list 2025-06-22 20:44:28 -04:00
80e0b03463 testing merge_flake_lock_update 2025-06-22 20:44:28 -04:00
747e2700ed updated update-flake-lock to be weakly 2025-06-21 23:57:18 -04:00
472f11e5b6 updated to --rebase 2025-06-21 22:20:13 -04:00
d75493997e testing auto-merge 2025-06-21 22:20:13 -04:00
d711983ac7 update desktop.nix kernelPackages to linuxPackages_6_14 2025-06-20 21:34:29 -04:00
1aabb2b112 removing wyoming 2025-06-19 15:17:54 -04:00
github-actions[bot]
8c09b8a78f flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/fe13e6abfe72b39ad8381595c3c404849330c3cb?dir=pkgs/firefox-addons&narHash=sha256-bN4tccrmczfR4PUuepHpxNNmWG3cLZTFIt4BaD8YyvA%3D' (2025-06-09)
  → 'gitlab:rycee/nur-expressions/a00ce73b626ed274fbfe9f51627861e140b08f6d?dir=pkgs/firefox-addons&narHash=sha256-b3y7V7db0VwLGtpcLRmT1Aa9dpAKoHQdem55UhgB/fw%3D' (2025-06-18)
• Updated input 'home-manager':
    'github:nix-community/home-manager/1df816c407d3a5090c8496c9b00170af7891f021?narHash=sha256-V1BgwiX8NjbRreU6LC2EzmuqFSQAHhoSeNlYJyZ40NE%3D' (2025-06-09)
  → 'github:nix-community/home-manager/0f355844e54e4c70906b1ef5cc35a0047d666c04?narHash=sha256-upC/GIlsIgtdtWRGd1obzdXWYQptNkfzZeyAFWgsgf0%3D' (2025-06-18)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/4602f7e1d3f197b3cb540d5accf5669121629628?narHash=sha256-W5GKQHgunda/OP9sbKENBZhMBDNu2QahoIPwnsF6CeM%3D' (2025-06-06)
  → 'github:nixos/nixos-hardware/61837d2a33ccc1582c5fabb7bf9130d39fee59ad?narHash=sha256-ynqbgIYrg7P1fAKYqe8I/PMiLABBcNDYG9YaAP/d/C4%3D' (2025-06-16)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/3e3afe5174c561dee0df6f2c2b2236990146329f?narHash=sha256-frdhQvPbmDYaScPFiCnfdh3B/Vh81Uuoo0w5TkWmmjU%3D' (2025-06-07)
  → 'github:nixos/nixpkgs/9e83b64f727c88a7711a2c463a7b16eedb69a84c?narHash=sha256-v263g4GbxXv87hMXMCpjkIxd/viIF7p3JpJrwgKdNiI%3D' (2025-06-17)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/8b64795ee08f70b73d4eeac16af0869f92b18284?narHash=sha256-G3nP/Eaymj55jV9p/xjeRmMTUICICGTNFpJaXDAoNtQ%3D' (2025-06-09)
  → 'github:nixos/nixpkgs/ba92ab5dc0759a8740003ca34b5c1b888f4766d4?narHash=sha256-JW40%2BzIiDS%2BrZavb9IYdIN40/GmErO2%2B0%2BA66rM6/b8%3D' (2025-06-19)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/8d215e1c981be3aa37e47aeabd4e61bb069548fd?narHash=sha256-lAblXm0VwifYCJ/ILPXJwlz0qNY07DDYdLD%2B9H%2BWc8o%3D' (2025-05-18)
  → 'github:Mic92/sops-nix/77c423a03b9b2b79709ea2cb63336312e78b72e2?narHash=sha256-Rr7Pooz9zQbhdVxux16h7URa6mA80Pb/G07T4lHvh0M%3D' (2025-06-17)
2025-06-19 00:27:57 -04:00
0fe99d9d4e updated shellAliases 2025-06-11 22:50:43 -04:00
148722be43 disabled sudo 2025-06-11 22:50:43 -04:00
be2421e3dc testing sudo-rs 2025-06-11 22:50:43 -04:00
2eb6c43b49 fixed formatting 2025-06-11 22:47:44 -04:00
897e06f622 added local for gcw and setup setup gcw acces for megan 2025-06-11 22:47:44 -04:00
4e99c54c12 worked with megan 2025-06-11 22:47:44 -04:00
3abd04ec5e added meg and gcw to postgres 2025-06-11 22:47:44 -04:00
a6a0fd0727 added gcw user 2025-06-11 22:47:44 -04:00
f23d381895 added user for megan 2025-06-11 22:47:44 -04:00
65335d1d38 adding great_cloud_of_witnesses.nix 2025-06-11 22:47:44 -04:00
github-actions[bot]
7b318e038a flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/493bb7da63143e3144c964d2b8e34e78dfe02a4b?dir=pkgs/firefox-addons&narHash=sha256-mk4nReFlqtGToFfZngNZ58FYud/svkmRp%2BTHBsfqhcw%3D' (2025-05-30)
  → 'gitlab:rycee/nur-expressions/fe13e6abfe72b39ad8381595c3c404849330c3cb?dir=pkgs/firefox-addons&narHash=sha256-bN4tccrmczfR4PUuepHpxNNmWG3cLZTFIt4BaD8YyvA%3D' (2025-06-09)
• Updated input 'home-manager':
    'github:nix-community/home-manager/7c60ea029602851cdeb2f3246e991fcc117195bc?narHash=sha256-5mhG43yYEEpLxEp6e683A8YiW4JHmWihF7XECjMM6Ns%3D' (2025-05-30)
  → 'github:nix-community/home-manager/1df816c407d3a5090c8496c9b00170af7891f021?narHash=sha256-V1BgwiX8NjbRreU6LC2EzmuqFSQAHhoSeNlYJyZ40NE%3D' (2025-06-09)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/daa628a725ab4948e0e2b795e8fb6f4c3e289a7a?narHash=sha256-pZH4bqbOd8S%2Bsi6UcfjHovWDiWKiIGRNRMpmRWaDIms%3D' (2025-05-30)
  → 'github:nixos/nixos-hardware/4602f7e1d3f197b3cb540d5accf5669121629628?narHash=sha256-W5GKQHgunda/OP9sbKENBZhMBDNu2QahoIPwnsF6CeM%3D' (2025-06-06)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/96ec055edbe5ee227f28cdbc3f1ddf1df5965102?narHash=sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg%3D' (2025-05-28)
  → 'github:nixos/nixpkgs/3e3afe5174c561dee0df6f2c2b2236990146329f?narHash=sha256-frdhQvPbmDYaScPFiCnfdh3B/Vh81Uuoo0w5TkWmmjU%3D' (2025-06-07)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/c936d57413b770679543a3aa85791f299b931a7b?narHash=sha256-SLAhuAJv0hYwRSHdbdTX9JFNl5c3I0NpQP8CY2xta6s%3D' (2025-05-31)
  → 'github:nixos/nixpkgs/8b64795ee08f70b73d4eeac16af0869f92b18284?narHash=sha256-G3nP/Eaymj55jV9p/xjeRmMTUICICGTNFpJaXDAoNtQ%3D' (2025-06-09)
2025-06-09 19:17:53 -04:00
2fe3ad1f68 ran treefmt 2025-06-08 15:06:01 -04:00
fa27adb4fe removed cache.tmmworkshop.com from nix.nix and flake.nix 2025-06-08 15:06:01 -04:00
ac7aec4efd update i[ address 2025-06-08 15:06:01 -04:00
87bebda02e removing duckdns 2025-06-03 18:06:17 -04:00
43276a693a updated defaultLocale to en_US.UTF-8 2025-05-31 09:09:12 -04:00
84504c68b7 removed supportedLocales 2025-05-31 09:09:12 -04:00
github-actions[bot]
45267b5c59 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/d4b3ffe3e719e42b175ccdef13598516f0a8025d?dir=pkgs/firefox-addons&narHash=sha256-d6SvA0gTHDrOqt4tZRVD0Gm5G4w6jAFJ6lis79PjSPw%3D' (2025-05-12)
  → 'gitlab:rycee/nur-expressions/493bb7da63143e3144c964d2b8e34e78dfe02a4b?dir=pkgs/firefox-addons&narHash=sha256-mk4nReFlqtGToFfZngNZ58FYud/svkmRp%2BTHBsfqhcw%3D' (2025-05-30)
• Updated input 'home-manager':
    'github:nix-community/home-manager/f0a7db5ec1d369721e770a45e4d19f8e48186a69?narHash=sha256-VnR33UmH0KzvTuVg%2B6oYkDVpnPuHanQisNUXytCRBPQ%3D' (2025-05-12)
  → 'github:nix-community/home-manager/7c60ea029602851cdeb2f3246e991fcc117195bc?narHash=sha256-5mhG43yYEEpLxEp6e683A8YiW4JHmWihF7XECjMM6Ns%3D' (2025-05-30)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/d1d68fe8b00248caaa5b3bbe4984c12b47e0867d?narHash=sha256-dMx20S2molwqJxbmMB4pGjNfgp5H1IOHNa1Eby6xL%2B0%3D' (2025-05-12)
  → 'github:nixos/nixos-hardware/daa628a725ab4948e0e2b795e8fb6f4c3e289a7a?narHash=sha256-pZH4bqbOd8S%2Bsi6UcfjHovWDiWKiIGRNRMpmRWaDIms%3D' (2025-05-30)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/d89fc19e405cb2d55ce7cc114356846a0ee5e956?narHash=sha256-3e%2BAVBczosP5dCLQmMoMEogM57gmZ2qrVSrmq9aResQ%3D' (2025-05-10)
  → 'github:nixos/nixpkgs/96ec055edbe5ee227f28cdbc3f1ddf1df5965102?narHash=sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg%3D' (2025-05-28)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/bee20293bce36bd20f2ed6627a60db76eaa7e9b4?narHash=sha256-WJ55VutSzEjVjq9jYpUvJp6fRB8ncgVzFpWoAwfvppg%3D' (2025-05-13)
  → 'github:nixos/nixpkgs/c936d57413b770679543a3aa85791f299b931a7b?narHash=sha256-SLAhuAJv0hYwRSHdbdTX9JFNl5c3I0NpQP8CY2xta6s%3D' (2025-05-31)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/e93ee1d900ad264d65e9701a5c6f895683433386?narHash=sha256-PxrrSFLaC7YuItShxmYbMgSuFFuwxBB%2Bqsl9BZUnRvg%3D' (2025-05-05)
  → 'github:Mic92/sops-nix/8d215e1c981be3aa37e47aeabd4e61bb069548fd?narHash=sha256-lAblXm0VwifYCJ/ILPXJwlz0qNY07DDYdLD%2B9H%2BWc8o%3D' (2025-05-18)
• Updated input 'system_tools':
    'github:RichieCahill/system_tools/c9979e045bca52ec85a0dc560b238f3e5ae2f01c?narHash=sha256-PM7dz99nb6dDiw/3naRGB/dUl5U7dJVspR9uevhW3xo%3D' (2025-03-10)
  → 'github:RichieCahill/system_tools/68ab5d1c17ac3fe2487f73dbbb4848bd2291139e?narHash=sha256-woyaUwmZurfNTXBEFM6M7ueSd/Udixs%2B4DUInhL835c%3D' (2025-05-17)
• Removed input 'system_tools/flake-utils'
• Removed input 'system_tools/flake-utils/systems'
• Removed input 'system_tools/poetry2nix'
• Removed input 'system_tools/poetry2nix/flake-utils'
• Removed input 'system_tools/poetry2nix/nix-github-actions'
• Removed input 'system_tools/poetry2nix/nix-github-actions/nixpkgs'
• Removed input 'system_tools/poetry2nix/nixpkgs'
• Removed input 'system_tools/poetry2nix/systems'
• Removed input 'system_tools/poetry2nix/treefmt-nix'
• Removed input 'system_tools/poetry2nix/treefmt-nix/nixpkgs'
• Added input 'system_tools/pyproject-build-systems':
    'github:pyproject-nix/build-system-pkgs/7dba6dbc73120e15b558754c26024f6c93015dd7?narHash=sha256-nysSwVVjG4hKoOjhjvE6U5lIKA8sEr1d1QzEfZsannU%3D' (2025-04-14)
• Added input 'system_tools/pyproject-build-systems/nixpkgs':
    follows 'system_tools/nixpkgs'
• Added input 'system_tools/pyproject-build-systems/pyproject-nix':
    follows 'system_tools/pyproject-nix'
• Added input 'system_tools/pyproject-build-systems/uv2nix':
    follows 'system_tools/uv2nix'
• Added input 'system_tools/pyproject-nix':
    'github:pyproject-nix/pyproject.nix/e09c10c24ebb955125fda449939bfba664c467fd?narHash=sha256-QxdHGNpbicIrw5t6U3x%2BZxeY/7IEJ6lYbvsjXmcxFIM%3D' (2025-05-06)
• Added input 'system_tools/pyproject-nix/nixpkgs':
    follows 'system_tools/nixpkgs'
• Added input 'system_tools/uv2nix':
    'github:pyproject-nix/uv2nix/582024dc64663e9f88d467c2f7f7b20d278349de?narHash=sha256-W8BFXk5R0TuJcjIhcGoMpSOaIufGXpizK0pm%2BuTqynA%3D' (2025-05-17)
• Added input 'system_tools/uv2nix/nixpkgs':
    follows 'system_tools/nixpkgs'
• Added input 'system_tools/uv2nix/pyproject-nix':
    follows 'system_tools/pyproject-nix'
2025-05-30 22:44:24 -04:00
github-actions[bot]
bd964a8390 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/0388c30f59e860307b2ef0ba93f38d2e525a153e?dir=pkgs/firefox-addons&narHash=sha256-vX5uq4wv7FeNeeFtsTPXQ9n2hd92NFXI%2Bu9sg1K8OhA%3D' (2025-04-27)
  → 'gitlab:rycee/nur-expressions/d4b3ffe3e719e42b175ccdef13598516f0a8025d?dir=pkgs/firefox-addons&narHash=sha256-d6SvA0gTHDrOqt4tZRVD0Gm5G4w6jAFJ6lis79PjSPw%3D' (2025-05-12)
• Updated input 'home-manager':
    'github:nix-community/home-manager/5f217e5a319f6c186283b530f8c975e66c028433?narHash=sha256-osgPX/SzIpkR50vev/rqoTEAVkEcOWXoQXmbzsaI4KU%3D' (2025-04-30)
  → 'github:nix-community/home-manager/f0a7db5ec1d369721e770a45e4d19f8e48186a69?narHash=sha256-VnR33UmH0KzvTuVg%2B6oYkDVpnPuHanQisNUXytCRBPQ%3D' (2025-05-12)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/72081c9fbbef63765ae82bff9727ea79cc86bd5b?narHash=sha256-mmV2oPhQN%2BYF2wmnJzXX8tqgYmUYXUj3uUUBSTmYN5o%3D' (2025-04-29)
  → 'github:nixos/nixos-hardware/d1d68fe8b00248caaa5b3bbe4984c12b47e0867d?narHash=sha256-dMx20S2molwqJxbmMB4pGjNfgp5H1IOHNa1Eby6xL%2B0%3D' (2025-05-12)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/46e634be05ce9dc6d4db8e664515ba10b78151ae?narHash=sha256-y3h3NLnzRSiUkYpnfvnS669zWZLoqqI6NprtLQ%2B5dck%3D' (2025-04-29)
  → 'github:nixos/nixpkgs/d89fc19e405cb2d55ce7cc114356846a0ee5e956?narHash=sha256-3e%2BAVBczosP5dCLQmMoMEogM57gmZ2qrVSrmq9aResQ%3D' (2025-05-10)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/268ec3b4f1976d689f80b5b18da885420d258b0a?narHash=sha256-xZkR328WwOXlqI%2BKfksGTwyzQpRICmR4wUrxk6pbWqg%3D' (2025-05-01)
  → 'github:nixos/nixpkgs/bee20293bce36bd20f2ed6627a60db76eaa7e9b4?narHash=sha256-WJ55VutSzEjVjq9jYpUvJp6fRB8ncgVzFpWoAwfvppg%3D' (2025-05-13)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/5e3e92b16d6fdf9923425a8d4df7496b2434f39c?narHash=sha256-ePyTpKEJTgX0gvgNQWd7tQYQ3glIkbqcW778RpHlqgA%3D' (2025-04-22)
  → 'github:Mic92/sops-nix/e93ee1d900ad264d65e9701a5c6f895683433386?narHash=sha256-PxrrSFLaC7YuItShxmYbMgSuFFuwxBB%2Bqsl9BZUnRvg%3D' (2025-05-05)
2025-05-13 18:27:42 -04:00
github-actions[bot]
86e90440c4 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/89b9fcf0f026688036283f5716677817a7cec061?dir=pkgs/firefox-addons&narHash=sha256-Ch8OueK%2BUNc4lEkUbL9K9xgBXEKVEsB7GrNZm6jO46s%3D' (2025-04-13)
  → 'gitlab:rycee/nur-expressions/0388c30f59e860307b2ef0ba93f38d2e525a153e?dir=pkgs/firefox-addons&narHash=sha256-vX5uq4wv7FeNeeFtsTPXQ9n2hd92NFXI%2Bu9sg1K8OhA%3D' (2025-04-27)
• Updated input 'home-manager':
    'github:nix-community/home-manager/cfa196c705a896372319249d757085876ab62448?narHash=sha256-g07PYyZCIHVDLzRo8fllZRES7Nf9R8%2BxZwNJ7t0gt5s%3D' (2025-04-13)
  → 'github:nix-community/home-manager/5f217e5a319f6c186283b530f8c975e66c028433?narHash=sha256-osgPX/SzIpkR50vev/rqoTEAVkEcOWXoQXmbzsaI4KU%3D' (2025-04-30)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/1fe3cc2bc5d2dc9c81cb4e63d2f67c1543340df1?narHash=sha256-OuLhysErPHl53BBifhesrRumJNhrlSgQDfYOTXfgIMg%3D' (2025-04-11)
  → 'github:nixos/nixos-hardware/72081c9fbbef63765ae82bff9727ea79cc86bd5b?narHash=sha256-mmV2oPhQN%2BYF2wmnJzXX8tqgYmUYXUj3uUUBSTmYN5o%3D' (2025-04-29)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/2631b0b7abcea6e640ce31cd78ea58910d31e650?narHash=sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR%2BXhw3kr/3Xd0GPTM%3D' (2025-04-12)
  → 'github:nixos/nixpkgs/46e634be05ce9dc6d4db8e664515ba10b78151ae?narHash=sha256-y3h3NLnzRSiUkYpnfvnS669zWZLoqqI6NprtLQ%2B5dck%3D' (2025-04-29)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/fe24d8526fe90e49fd04bd36e18722a1894ea41f?narHash=sha256-2rcE3NDqQVYjdrUHFFgJSJG7ATUkJzAFaBz8ybTp2fU%3D' (2025-04-13)
  → 'github:nixos/nixpkgs/268ec3b4f1976d689f80b5b18da885420d258b0a?narHash=sha256-xZkR328WwOXlqI%2BKfksGTwyzQpRICmR4wUrxk6pbWqg%3D' (2025-05-01)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/7e147a1ae90f0d4a374938cdc3df3cdaecb9d388?narHash=sha256-lv52pnfiRGp5%2BxkZEgWr56DWiRgkMFXpiGba3eJ3krE%3D' (2025-04-13)
  → 'github:Mic92/sops-nix/5e3e92b16d6fdf9923425a8d4df7496b2434f39c?narHash=sha256-ePyTpKEJTgX0gvgNQWd7tQYQ3glIkbqcW778RpHlqgA%3D' (2025-04-22)
2025-04-30 20:23:43 -04:00
github-actions[bot]
2b743fa1b8 flake.lock: Update
Flake lock file updates:

• Updated input 'firefox-addons':
    'gitlab:rycee/nur-expressions/f8861a4b09a181dd88f6626d0202d9225ae85d65?dir=pkgs/firefox-addons&narHash=sha256-tlJY7MfAena/yi3lmd7y7vQGpLma4Q1BLtO4dvzF/Vs%3D' (2025-03-27)
  → 'gitlab:rycee/nur-expressions/89b9fcf0f026688036283f5716677817a7cec061?dir=pkgs/firefox-addons&narHash=sha256-Ch8OueK%2BUNc4lEkUbL9K9xgBXEKVEsB7GrNZm6jO46s%3D' (2025-04-13)
• Removed input 'firefox-addons/flake-utils'
• Updated input 'home-manager':
    'github:nix-community/home-manager/b431496538b0e294fbe44a1441b24ae8195c63f0?narHash=sha256-G7866vbO5jgqMcYJzgbxej40O6mBGQMGt6gM0himjoA%3D' (2025-03-29)
  → 'github:nix-community/home-manager/cfa196c705a896372319249d757085876ab62448?narHash=sha256-g07PYyZCIHVDLzRo8fllZRES7Nf9R8%2BxZwNJ7t0gt5s%3D' (2025-04-13)
• Updated input 'nixos-hardware':
    'github:nixos/nixos-hardware/0ed819e708af17bfc4bbc63ee080ef308a24aa42?narHash=sha256-I09SrXIO0UdyBFfh0fxDq5WnCDg8XKmZ1HQbaXzMA1k%3D' (2025-03-28)
  → 'github:nixos/nixos-hardware/1fe3cc2bc5d2dc9c81cb4e63d2f67c1543340df1?narHash=sha256-OuLhysErPHl53BBifhesrRumJNhrlSgQDfYOTXfgIMg%3D' (2025-04-11)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/5e5402ecbcb27af32284d4a62553c019a3a49ea6?narHash=sha256-gWd4urRoLRe8GLVC/3rYRae1h%2BxfQzt09xOfb0PaHSk%3D' (2025-03-27)
  → 'github:nixos/nixpkgs/2631b0b7abcea6e640ce31cd78ea58910d31e650?narHash=sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR%2BXhw3kr/3Xd0GPTM%3D' (2025-04-12)
• Updated input 'nixpkgs-master':
    'github:nixos/nixpkgs/a2cd41ef5ba904d58f230740f06567ff9506e368?narHash=sha256-UTEKMh3sbRHVr6ZYIoM7NWQjfrjIGTVOoV2Q4LkLvSk%3D' (2025-03-30)
  → 'github:nixos/nixpkgs/fe24d8526fe90e49fd04bd36e18722a1894ea41f?narHash=sha256-2rcE3NDqQVYjdrUHFFgJSJG7ATUkJzAFaBz8ybTp2fU%3D' (2025-04-13)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/67566fe68a8bed2a7b1175fdfb0697ed22ae8852?narHash=sha256-ZGlpUDsuBdeZeTNgoMv%2Baw0ByXT2J3wkYw9kJwkAS4M%3D' (2025-03-23)
  → 'github:Mic92/sops-nix/7e147a1ae90f0d4a374938cdc3df3cdaecb9d388?narHash=sha256-lv52pnfiRGp5%2BxkZEgWr56DWiRgkMFXpiGba3eJ3krE%3D' (2025-04-13)
2025-04-13 17:30:00 -04:00
152 changed files with 5864 additions and 383 deletions

View File

@@ -15,12 +15,14 @@ jobs:
matrix:
system:
- "bob"
- "brain"
- "jeeves"
- "leviathan"
- "rhapsody-in-green"
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Build default package
run: "nixos-rebuild build --flake ./#${{ matrix.system }}"
- name: copy to nix-cache
run: nix copy --to ssh://jeeves .#nixosConfigurations.${{ matrix.system }}.config.system.build.toplevel

View File

@@ -0,0 +1,29 @@
name: merge_flake_lock_update
on:
workflow_dispatch:
schedule:
- cron: "0 2 * * 6"
jobs:
merge:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: merge_flake_lock_update
run: |
pr_number=$(gh pr list --state open --author RichieCahill --label flake_lock_update --json number --jq '.[0].number')
echo "pr_number=$pr_number" >> $GITHUB_ENV
if [ -n "$pr_number" ]; then
gh pr merge "$pr_number" --rebase
else
echo "No open PR found with label flake_lock_update"
fi
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_FOR_UPDATES }}

19
.github/workflows/pytest.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: pytest
on:
push:
branches:
- main
pull_request:
branches:
- main
merge_group:
jobs:
pytest:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: Run tests
run: nix develop .#devShells.x86_64-linux.default -c pytest tests

View File

@@ -2,7 +2,7 @@ name: update-flake-lock
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * *"
- cron: "0 0 * * 6"
jobs:
lockfile:
@@ -20,3 +20,4 @@ jobs:
pr-labels: |
dependencies
automated
flake_lock_update

View File

@@ -1,9 +1,13 @@
# Generate AGE keys from SSH keys with:
# ssh-keygen -A
# nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age'
keys:
- &admin_richie age1u8zj599elqqvcmhxn8zuwrufsz8w8w366d3ayrljjejljt2q45kq8mxw9c # cspell:disable-line
- &system_bob age1q47vup0tjhulkg7d6xwmdsgrw64h4ax3la3evzqpxyy4adsmk9fs56qz3y # cspell:disable-line
- &system_brain age1jhf7vm0005j60mjq63696frrmjhpy8kpc2d66mw044lqap5mjv4snmwvwm # cspell:disable-line
- &system_jeeves age13lmqgc3jvkyah5e3vcwmj4s5wsc2akctcga0lpc0x8v8du3fxprqp4ldkv # cspell:disable-line
- &system_router age1xzxryqq63x65yuza9lmmkud7crjjxpnkdew070yhx6xn7xe4tdws5twxsv # cspell:disable-line
- &system_leviathan age1l272y8udvg60z7edgje42fu49uwt4x2gxn5zvywssnv9h2krms8s094m4k # cspell:disable-line
- &system_rhapsody age1ufnewppysaq2wwcl4ugngjz8pfzc5a35yg7luq0qmuqvctajcycs5lf6k4 # cspell:disable-line
creation_rules:
@@ -12,6 +16,7 @@ creation_rules:
- age:
- *admin_richie
- *system_bob
- *system_brain
- *system_jeeves
- *system_router
- *system_leviathan
- *system_rhapsody

27
.vscode/settings.json vendored
View File

@@ -2,6 +2,7 @@
"cSpell.words": [
"aboutwelcome",
"acltype",
"addopts",
"addstr",
"advplyr",
"ahci",
@@ -9,6 +10,7 @@
"aiounifi",
"alsa",
"apiclient",
"apscheduler",
"archlinux",
"ashift",
"asrouter",
@@ -114,6 +116,7 @@
"httpchk",
"hurlenko",
"hwloc",
"ignorelist",
"INITDB",
"iocharset",
"ioit",
@@ -148,11 +151,15 @@
"mixtral",
"mklabel",
"mkpart",
"modbus",
"modbuss",
"modesetting",
"mountpoint",
"mountpoints",
"mousewheel",
"mqtt",
"mtxr",
"mypy",
"ncdu",
"nemo",
"neofetch",
@@ -184,6 +191,7 @@
"overalljails",
"overscroll",
"overseerr",
"paho",
"partitionwise",
"pbmode",
"pciutils",
@@ -211,9 +219,14 @@
"pulseaudio",
"punycode",
"pychromecast",
"pydocstyle",
"pyfakefs",
"pylance",
"pylint",
"pymetno",
"pymodbus",
"pyownet",
"pytest",
"qbit",
"qbittorrent",
"qbittorrentvpn",
@@ -239,6 +252,7 @@
"schemeless",
"scrollback",
"SECUREFOX",
"sessionmaker",
"sessionstore",
"shellcheck",
"signon",
@@ -250,6 +264,7 @@
"socialtracking",
"sonarr",
"sponsorblock",
"sqlalchemy",
"sqltools",
"ssdp",
"SSHOPTS",
@@ -261,6 +276,7 @@
"tabmanager",
"tamasfe",
"TCPIP",
"testdisk",
"tiktok",
"timonwong",
"titlebar",
@@ -270,6 +286,7 @@
"topstories",
"treefmt",
"twimg",
"typer",
"uaccess",
"ublock",
"uiprotect",
@@ -285,6 +302,7 @@
"usernamehw",
"userprefs",
"vfat",
"victron",
"virt",
"virtualisation",
"vpnpromourl",
@@ -296,6 +314,8 @@
"wireshark",
"Workqueues",
"xattr",
"xcursorgen",
"xdist",
"xhci",
"yazi",
"yubikey",
@@ -307,5 +327,10 @@
"zoxide",
"zram",
"zstd"
]
],
"python-envs.defaultEnvManager": "ms-python.python:system",
"python-envs.pythonProjects": [],
"python.testing.pytestArgs": ["tests"],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}

View File

@@ -22,7 +22,22 @@
boot = {
tmp.useTmpfs = true;
kernelPackages = lib.mkDefault pkgs.linuxPackages_6_12;
kernelPackages = lib.mkDefault (
pkgs.linuxPackages_6_12.extend (
self: super: {
kernel = super.kernel.override {
argsOverride = {
version = "6.12.52";
modDirVersion = "6.12.52";
src = pkgs.fetchurl {
url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.52.tar.xz";
sha256 = "sha256-tIUM9nCgMscPOLcTon1iBGxfdHyvAoxfULGPmGBqnrE=";
};
};
};
}
)
);
zfs.package = lib.mkDefault pkgs.zfs_2_3;
};
@@ -44,7 +59,10 @@
# firmware update
fwupd.enable = true;
snapshot_manager.enable = lib.mkDefault true;
snapshot_manager = {
enable = lib.mkDefault true;
PYTHONPATH = "${inputs.self}/";
};
zfs = {
trim.enable = lib.mkDefault true;
@@ -56,7 +74,14 @@
programs.zsh.enable = true;
security.auditd.enable = lib.mkDefault true;
security = {
auditd.enable = lib.mkDefault true;
sudo-rs = {
enable = true;
execWheelOnly = true;
};
sudo.enable = false;
};
users.mutableUsers = lib.mkDefault false;

View File

@@ -1,4 +1,10 @@
{ lib, pkgs, ... }:
let
libPath = pkgs.lib.makeLibraryPath [
pkgs.zlib
pkgs.stdenv.cc.cc.lib
];
in
{
programs.nix-ld = {
enable = lib.mkDefault true;
@@ -15,6 +21,7 @@
libxml2
openssl
stdenv.cc.cc
stdenv.cc.cc.lib
systemd
util-linux
xz
@@ -23,4 +30,9 @@
zstd
];
};
environment = {
sessionVariables.LD_LIBRARY_PATH = lib.mkDefault libPath;
variables.LD_LIBRARY_PATH = lib.mkDefault libPath;
};
}

View File

@@ -4,8 +4,7 @@
console.keyMap = lib.mkDefault "us";
i18n = {
defaultLocale = lib.mkDefault "en_US.utf8";
supportedLocales = lib.mkDefault [ "en_US.UTF-8/UTF-8" ];
defaultLocale = lib.mkDefault "en_US.UTF-8";
extraLocaleSettings = lib.mkDefault {
LC_ADDRESS = "en_US.UTF-8";
LC_IDENTIFICATION = "en_US.UTF-8";

View File

@@ -15,17 +15,14 @@ in
];
trusted-substituters = [
"https://cache.nixos.org"
"https://cache.tmmworkshop.com"
"https://nix-community.cachix.org"
];
substituters = [
"https://cache.nixos.org/?priority=2&want-mass-query=true"
"https://cache.tmmworkshop.com/?priority=2&want-mass-query=true"
"https://nix-community.cachix.org/?priority=10&want-mass-query=true"
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"cache.tmmworkshop.com:jHffkpgbmEdstQPoihJPYW9TQe6jnQbWR2LqkNGV3iA="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
auto-optimise-store = lib.mkDefault true;

View File

@@ -2,6 +2,6 @@
{
environment.systemPackages = with pkgs; [
git
python313
my_python
];
}

View File

@@ -1,5 +1,4 @@
{
inputs,
pkgs,
lib,
config,
@@ -11,33 +10,48 @@ in
{
options = {
services.snapshot_manager = {
enable = lib.mkOption {
default = true;
example = true;
description = "Whether to enable k3s-net.";
type = lib.types.bool;
};
enable = lib.mkEnableOption "ZFS snapshot manager";
path = lib.mkOption {
type = lib.types.path;
description = "Path that needs to be updated via git pull";
default = ./snapshot_config.toml;
description = "Path to the snapshot_manager TOML config.";
};
PYTHONPATH = lib.mkOption {
type = lib.types.str;
description = ''
the PYTHONPATH to use for the snapshot_manager service.
'';
};
EnvironmentFile = lib.mkOption {
type = lib.types.nullOr (lib.types.coercedTo lib.types.path toString lib.types.str);
default = null;
description = ''
Single environment file for the service (e.g. /etc/snapshot-manager/env).
Use a leading "-" to ignore if missing (systemd feature).
'';
};
};
};
config = lib.mkIf cfg.enable {
systemd = {
services."snapshot_manager" = {
services.snapshot_manager = {
description = "ZFS Snapshot Manager";
requires = [ "zfs-import.target" ];
after = [ "zfs-import.target" ];
path = [ pkgs.zfs ];
environment = {
PYTHONPATH = cfg.PYTHONPATH;
};
serviceConfig = {
Type = "oneshot";
ExecStart = "${inputs.system_tools.packages.x86_64-linux.default}/bin/snapshot_manager --config-file='${cfg.path}'";
ExecStart = "${pkgs.my_python}/bin/python -m python.tools.snapshot_manager ${lib.escapeShellArg cfg.path}";
}
// lib.optionalAttrs (cfg.EnvironmentFile != null) {
EnvironmentFile = cfg.EnvironmentFile;
};
};
timers."snapshot_manager" = {
timers.snapshot_manager = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "15m";

View File

@@ -37,6 +37,8 @@
TcpKeepAlive = "no";
X11Forwarding = lib.mkDefault false;
KexAlgorithms = [
"sntrup761x25519-sha512@openssh.com"
"mlkem768x25519-sha256"
"curve25519-sha256@libssh.org"
"diffie-hellman-group-exchange-sha256"
];

View File

@@ -1,8 +1,8 @@
{ pkgs, ... }:
{
boot = {
kernelPackages = pkgs.linuxPackages_6_13;
zfs.package = pkgs.zfs_2_3;
kernelPackages = pkgs.linuxPackages_6_17;
zfs.package = pkgs.zfs_unstable;
};
hardware.bluetooth = {

View File

@@ -10,6 +10,9 @@
authorizedKeys = config.users.users.richie.openssh.authorizedKeys.keys;
};
};
availableKernelModules = [ "igb" ];
availableKernelModules = [
"igb"
"r8152"
];
};
}

View File

@@ -8,10 +8,11 @@
dataDir = "/home/richie/Syncthing";
configDir = "/home/richie/.config/syncthing";
settings.devices = {
phone.id = "TBRULKD-7DZPGGZ-F6LLB7J-MSO54AY-7KLPBIN-QOFK6PX-W2HBEWI-PHM2CQI"; # cspell:disable-line
jeeves.id = "ICRHXZW-ECYJCUZ-I4CZ64R-3XRK7CG-LL2HAAK-FGOHD22-BQA4AI6-5OAL6AG"; # cspell:disable-line
ipad.id = "KI76T3X-SFUGV2L-VSNYTKR-TSIUV5L-SHWD3HE-GQRGRCN-GY4UFMD-CW6Z6AX"; # cspell:disable-line
bob.id = "CJIAPEJ-VO74RR4-F75VU6M-QNZAMYG-FYUJG7Y-6AT62HJ-355PRPL-PJFETAZ"; # cspell:disable-line
brain.id = "SSCGIPI-IV3VYKB-TRNIJE3-COV4T2H-CDBER7F-I2CGHYA-NWOEUDU-3T5QAAN"; # cspell:disable-line
ipad.id = "KI76T3X-SFUGV2L-VSNYTKR-TSIUV5L-SHWD3HE-GQRGRCN-GY4UFMD-CW6Z6AX"; # cspell:disable-line
jeeves.id = "ICRHXZW-ECYJCUZ-I4CZ64R-3XRK7CG-LL2HAAK-FGOHD22-BQA4AI6-5OAL6AG"; # cspell:disable-line
phone.id = "TBRULKD-7DZPGGZ-F6LLB7J-MSO54AY-7KLPBIN-QOFK6PX-W2HBEWI-PHM2CQI"; # cspell:disable-line
rhapsody-in-green.id = "ASL3KC4-3XEN6PA-7BQBRKE-A7JXLI6-DJT43BY-Q4WPOER-7UALUAZ-VTPQ6Q4"; # cspell:disable-line
};
};

View File

@@ -0,0 +1,7 @@
{
nix.settings = {
trusted-substituters = [ "http://cache.tmmworkshop.com" ];
substituters = [ "http://cache.tmmworkshop.com/?priority=1&want-mass-query=true" ];
trusted-public-keys = [ "cache.tmmworkshop.com:jHffkpgbmEdstQPoihJPYW9TQe6jnQbWR2LqkNGV3iA=" ];
};
}

View File

@@ -5,5 +5,7 @@
randomizedDelaySec = "1h";
persistent = true;
flake = "github:RichieCahill/dotfiles";
allowReboot = true;
dates = "Sat *-*-* 06:00:00";
};
}

4
docs/Gemfile Normal file
View File

@@ -0,0 +1,4 @@
source "https://rubygems.org"
# The github-pages gem pins all compatible versions of Jekyll and its plugins
gem "github-pages", group: :jekyll_plugins

23
docs/_config.yml Normal file
View File

@@ -0,0 +1,23 @@
title: "Richie Cahill"
description: "ALL THE CHAOS THAT I CANT DO AT WORK"
baseurl: "/dotfiles"
url: "https://richiecahill.github.io"
remote_theme: pages-themes/hacker@v0.2.0
plugins:
- jekyll-feed
- jekyll-remote-theme
- jekyll-seo-tag
- jekyll-sitemap
- jekyll-paginate
paginate: 5
paginate_path: "/page:num"
author:
name: "Richie Cahill"
email: "richie@tmmworkshop.com"
social_links:
github: "RichieCahill"
website: "https://tmmworkshop.com"

View File

@@ -0,0 +1,13 @@
# The MONOREPO experiment
Im testing a [MONOREPO](https://en.wikipedia.org/wiki/Monorepo) because Phil said this was a bad idea. To that i say hold my beer.
In all seriousness, I Think that for a small dev team/solo dev. The simplicity is worth higher barer to entry. One of my most annoying processes was updating my system tools. I had to build my update in a feature branch and then merge it into my main branch. then go to my dotfiles create a feature branch update the system tools merge it into main.
It will be starting with my Nix Dotfiles Python tools and now my blog.
I will be reaching ot to phil on 2030-10-31 and 2035-10-31 to give him updates on the progress.
Known Issues:
- the python tests are running on the current derivation not the one the derivation im updating to.

17
docs/index.md Normal file
View File

@@ -0,0 +1,17 @@
---
layout: default
title: "Welcome"
---
Welcome to my build logs, notes, and experiments.
You can read my latest posts below
<ul>
{% for post in site.posts %}
<li>
<a href="{{ post.url | relative_url }}">{{ post.title }}</a>
<small>— {{ post.date | date: "%Y-%m-%d" }}</small>
</li>
{% endfor %}
</ul>

3
esphome/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
# esphome
/.esphome/
/secrets.yaml

129
esphome/batteries.yml Normal file
View File

@@ -0,0 +1,129 @@
esphome:
name: batteries
friendly_name: batteries
esp32:
board: esp32dev
framework:
type: arduino
logger:
api:
encryption:
key: !secret api_key
external_components:
- source: github://syssi/esphome-jk-bms@main
ota:
- platform: esphome
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
captive_portal:
esp32_ble_tracker:
scan_parameters:
interval: 1100ms
window: 1100ms
active: true
ble_client:
- mac_address: "C8:47:80:29:0F:DB"
id: jk_ble0
- mac_address: "C8:47:80:37:9D:DD"
id: jk_ble1
jk_bms_ble:
- ble_client_id: jk_ble0
protocol_version: JK02_32S
throttle: 1s
id: jk_bms0
- ble_client_id: jk_ble1
protocol_version: JK02_32S
throttle: 1s
id: jk_bms1
sensor:
# BMS1 sensors
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms0
total_voltage:
name: "JK0 Total Voltage"
current:
name: "JK0 Current"
state_of_charge:
name: "JK0 SoC"
power:
name: "JK0 Power"
temperature_sensor_1:
name: "JK0 Temp 1"
temperature_sensor_2:
name: "JK0 Temp 2"
balancing:
name: "JK0 balancing"
charging_cycles:
name: "JK0 charging cycles"
total_runtime:
name: "JK0 total runtime"
balancing_current:
name: "JK0 balancing current"
# BMS2 sensors
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms1
total_voltage:
name: "JK1 Total Voltage"
current:
name: "JK1 Current"
state_of_charge:
name: "JK1 SoC"
power:
name: "Jk1 Power"
temperature_sensor_1:
name: "JK1 Temp 1"
temperature_sensor_2:
name: "Jk1 Temp 2"
balancing:
name: "JK1 balancing"
charging_cycles:
name: "JK1 charging cycles"
total_runtime:
name: "JK1 total runtime"
balancing_current:
name: "JK1 balancing current"
text_sensor:
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms0
errors:
name: "JK0 Errors"
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms1
errors:
name: "JK1 Errors"
switch:
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms0
charging:
name: "JK0 Charging"
discharging:
name: "JK0 Discharging"
balancer:
name: "JK0 Balancing"
- platform: jk_bms_ble
jk_bms_ble_id: jk_bms1
charging:
name: "JK1 Charging"
discharging:
name: "JK1 Discharging"
balancer:
name: "JK1 Balancing"

1
file_sizes.txt.new Normal file

File diff suppressed because one or more lines are too long

214
flake.lock generated
View File

@@ -2,18 +2,17 @@
"nodes": {
"firefox-addons": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"dir": "pkgs/firefox-addons",
"lastModified": 1743119709,
"narHash": "sha256-tlJY7MfAena/yi3lmd7y7vQGpLma4Q1BLtO4dvzF/Vs=",
"lastModified": 1763697825,
"narHash": "sha256-AgCCcVPOi1tuzuW5/StlwqBjRWSX62oL97qWuxrq5UA=",
"owner": "rycee",
"repo": "nur-expressions",
"rev": "f8861a4b09a181dd88f6626d0202d9225ae85d65",
"rev": "cefce78793603231be226fa77e7ad58e0e4899b8",
"type": "gitlab"
},
"original": {
@@ -23,39 +22,6 @@
"type": "gitlab"
}
},
"flake-utils": {
"locked": {
"lastModified": 1629284811,
"narHash": "sha256-JHgasjPR0/J1J3DRm4KxM4zTyAj4IOJY8vIl75v/kPI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c5d161cc0af116a2e17f54316f0bf43f0819785c",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
@@ -63,11 +29,11 @@
]
},
"locked": {
"lastModified": 1743267068,
"narHash": "sha256-G7866vbO5jgqMcYJzgbxej40O6mBGQMGt6gM0himjoA=",
"lastModified": 1763748372,
"narHash": "sha256-AUc78Qv3sWir0hvbmfXoZ7Jzq9VVL97l+sP9Jgms+JU=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "b431496538b0e294fbe44a1441b24ae8195c63f0",
"rev": "d10a9b16b2a3ee28433f3d1c603f4e9f1fecb8e1",
"type": "github"
},
"original": {
@@ -76,35 +42,13 @@
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"system_tools",
"poetry2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1729742964,
"narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "e04df33f62cdcf93d73e9a04142464753a16db67",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nixos-hardware": {
"locked": {
"lastModified": 1743167577,
"narHash": "sha256-I09SrXIO0UdyBFfh0fxDq5WnCDg8XKmZ1HQbaXzMA1k=",
"lastModified": 1762847253,
"narHash": "sha256-BWWnUUT01lPwCWUvS0p6Px5UOBFeXJ8jR+ZdLX8IbrU=",
"owner": "nixos",
"repo": "nixos-hardware",
"rev": "0ed819e708af17bfc4bbc63ee080ef308a24aa42",
"rev": "899dc449bc6428b9ee6b3b8f771ca2b0ef945ab9",
"type": "github"
},
"original": {
@@ -116,11 +60,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1743095683,
"narHash": "sha256-gWd4urRoLRe8GLVC/3rYRae1h+xfQzt09xOfb0PaHSk=",
"lastModified": 1763421233,
"narHash": "sha256-Stk9ZYRkGrnnpyJ4eqt9eQtdFWRRIvMxpNRf4sIegnw=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "5e5402ecbcb27af32284d4a62553c019a3a49ea6",
"rev": "89c2b2330e733d6cdb5eae7b899326930c2c0648",
"type": "github"
},
"original": {
@@ -130,13 +74,29 @@
"type": "github"
}
},
"nixpkgs-linux-firmware-20251011": {
"locked": {
"lastModified": 1760099975,
"narHash": "sha256-/fXH2TIVxVgmBbPouQNMsEPfUFB8Z9n6T9t40HMeC/k=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f9430900368d5a7346e30e6ecc7b26c9f7cc35cf",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f9430900368d5a7346e30e6ecc7b26c9f7cc35cf",
"type": "github"
}
},
"nixpkgs-master": {
"locked": {
"lastModified": 1743293199,
"narHash": "sha256-UTEKMh3sbRHVr6ZYIoM7NWQjfrjIGTVOoV2Q4LkLvSk=",
"lastModified": 1763774007,
"narHash": "sha256-PPeHfKA11P09kBkBD5pS3tIAFjnG5muHQnODQGTY87g=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a2cd41ef5ba904d58f230740f06567ff9506e368",
"rev": "8a7cf7e9e18384533d9ecd0bfbcf475ac1dc497e",
"type": "github"
},
"original": {
@@ -162,45 +122,17 @@
"type": "github"
}
},
"poetry2nix": {
"inputs": {
"flake-utils": [
"system_tools",
"flake-utils"
],
"nix-github-actions": "nix-github-actions",
"nixpkgs": [
"system_tools",
"nixpkgs"
],
"systems": "systems_2",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1736884309,
"narHash": "sha256-eiCqmKl0BIRiYk5/ZhZozwn4/7Km9CWTbc15Cv+VX5k=",
"owner": "nix-community",
"repo": "poetry2nix",
"rev": "75d0515332b7ca269f6d7abfd2c44c47a7cbca7b",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "poetry2nix",
"type": "github"
}
},
"root": {
"inputs": {
"firefox-addons": "firefox-addons",
"home-manager": "home-manager",
"nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs",
"nixpkgs-linux-firmware-20251011": "nixpkgs-linux-firmware-20251011",
"nixpkgs-master": "nixpkgs-master",
"nixpkgs-stable": "nixpkgs-stable",
"sops-nix": "sops-nix",
"system_tools": "system_tools",
"systems": "systems_3"
"systems": "systems"
}
},
"sops-nix": {
@@ -210,11 +142,11 @@
]
},
"locked": {
"lastModified": 1742700801,
"narHash": "sha256-ZGlpUDsuBdeZeTNgoMv+aw0ByXT2J3wkYw9kJwkAS4M=",
"lastModified": 1763607916,
"narHash": "sha256-VefBA1JWRXM929mBAFohFUtQJLUnEwZ2vmYUNkFnSjE=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "67566fe68a8bed2a7b1175fdfb0697ed22ae8852",
"rev": "877bb495a6f8faf0d89fc10bd142c4b7ed2bcc0b",
"type": "github"
},
"original": {
@@ -223,59 +155,7 @@
"type": "github"
}
},
"system_tools": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": [
"nixpkgs"
],
"poetry2nix": "poetry2nix"
},
"locked": {
"lastModified": 1741571944,
"narHash": "sha256-PM7dz99nb6dDiw/3naRGB/dUl5U7dJVspR9uevhW3xo=",
"owner": "RichieCahill",
"repo": "system_tools",
"rev": "c9979e045bca52ec85a0dc560b238f3e5ae2f01c",
"type": "github"
},
"original": {
"owner": "RichieCahill",
"repo": "system_tools",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_3": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
@@ -289,28 +169,6 @@
"repo": "default-linux",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"system_tools",
"poetry2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1730120726,
"narHash": "sha256-LqHYIxMrl/1p3/kvm2ir925tZ8DkI0KA10djk8wecSk=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "9ef337e492a5555d8e17a51c911ff1f02635be15",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",

View File

@@ -4,12 +4,10 @@
nixConfig = {
extra-substituters = [
"https://cache.nixos.org/?priority=2&want-mass-query=true"
"https://cache.tmmworkshop.com/?priority=2&want-mass-query=true"
"https://nix-community.cachix.org/?priority=10&want-mass-query=true"
];
extra-trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" # cspell:disable-line
"cache.tmmworkshop.com:jHffkpgbmEdstQPoihJPYW9TQe6jnQbWR2LqkNGV3iA=" # cspell:disable-line
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" # cspell:disable-line
"cache-nix-dot:Od9KN34LXc6Lu7y1ozzV1kIXZa8coClozgth/SYE7dU=" # cspell:disable-line
];
@@ -21,6 +19,8 @@
nixpkgs-master.url = "github:nixos/nixpkgs/master";
systems.url = "github:nix-systems/default-linux";
nixpkgs-linux-firmware-20251011.url = "github:NixOS/nixpkgs/f9430900368d5a7346e30e6ecc7b26c9f7cc35cf";
nixos-hardware.url = "github:nixos/nixos-hardware/master";
home-manager = {
@@ -33,11 +33,6 @@
inputs.nixpkgs.follows = "nixpkgs";
};
system_tools = {
url = "github:RichieCahill/system_tools";
inputs.nixpkgs.follows = "nixpkgs";
};
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
@@ -61,6 +56,7 @@
system:
import nixpkgs {
inherit system;
overlays = builtins.attrValues outputs.overlays;
config.allowUnfree = true;
}
);
@@ -79,9 +75,16 @@
];
specialArgs = { inherit inputs outputs; };
};
brain = lib.nixosSystem {
modules = [
./systems/brain
];
specialArgs = { inherit inputs outputs; };
};
jeeves = lib.nixosSystem {
modules = [
./systems/jeeves
./systems/jeeves/linux-firmware-20251011.nix
];
specialArgs = { inherit inputs outputs; };
};
@@ -91,6 +94,12 @@
];
specialArgs = { inherit inputs outputs; };
};
leviathan = lib.nixosSystem {
modules = [
./systems/leviathan
];
specialArgs = { inherit inputs outputs; };
};
};
};
}

View File

@@ -14,4 +14,26 @@
config.allowUnfree = true;
};
};
python-env = final: _prev: {
my_python = final.python313.withPackages (
ps: with ps; [
apprise
apscheduler
mypy
polars
psycopg
pyfakefs
pytest
pytest-cov
pytest-mock
pytest-xdist
requests
ruff
sqlalchemy
typer
types-requests
]
);
};
}

73
pyproject.toml Normal file
View File

@@ -0,0 +1,73 @@
[project]
name = "system_tools"
version = "0.1.0"
description = ""
authors = [{ name = "Richie Cahill", email = "richie@tmmworkshop.com" }]
requires-python = "~=3.13.0"
readme = "README.md"
license = "MIT"
# these dependencies are a best effort and aren't guaranteed to work
dependencies = ["apprise", "apscheduler", "polars", "requests", "typer"]
[dependency-groups]
dev = [
"mypy",
"pyfakefs",
"pytest-cov",
"pytest-mock",
"pytest-xdist",
"pytest",
"ruff",
"types-requests",
]
[tool.ruff]
target-version = "py313"
line-length = 120
lint.select = ["ALL"]
lint.ignore = [
"G004", # (PERM) This is a performers nit
"COM812", # (TEMP) conflicts when used with the formatter
"ISC001", # (TEMP) conflicts when used with the formatter
"S603", # (PERM) This is known to cause a false positive
]
[tool.ruff.lint.per-file-ignores]
"tests/**" = [
"S101", # (perm) pytest needs asserts
]
"python/random/**" = [
"T201", # (perm) I don't care about print statements dir
]
"python/testing/**" = [
"T201", # (perm) I don't care about print statements dir
"ERA001", # (perm) I don't care about print statements dir
]
[tool.ruff.lint.pydocstyle]
convention = "google"
[tool.ruff.lint.flake8-builtins]
builtins-ignorelist = ["id"]
[tool.ruff.lint.pylint]
max-args = 9
[tool.coverage.run]
source = ["system_tools"]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"if TYPE_CHECKING:",
"raise NotImplementedError",
"if __name__ == \"__main__\":",
]
[tool.pytest.ini_options]
addopts = "-n auto -ra"
# --cov=system_tools --cov-report=term-missing --cov-report=xml --cov-report=html --cov-branch

1
python/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Server Tools."""

72
python/common.py Normal file
View File

@@ -0,0 +1,72 @@
"""common."""
from __future__ import annotations
import logging
import sys
from datetime import UTC, datetime
from os import getenv
from subprocess import PIPE, Popen
from apprise import Apprise
logger = logging.getLogger(__name__)
def configure_logger(level: str = "INFO") -> None:
"""Configure the logger.
Args:
level (str, optional): The logging level. Defaults to "INFO".
"""
logging.basicConfig(
level=level,
datefmt="%Y-%m-%dT%H:%M:%S%z",
format="%(asctime)s %(levelname)s %(filename)s:%(lineno)d - %(message)s",
handlers=[logging.StreamHandler(sys.stdout)],
)
def bash_wrapper(command: str) -> tuple[str, int]:
"""Execute a bash command and capture the output.
Args:
command (str): The bash command to be executed.
Returns:
Tuple[str, int]: A tuple containing the output of the command (stdout) as a string,
the error output (stderr) as a string (optional), and the return code as an integer.
"""
# This is a acceptable risk
process = Popen(command.split(), stdout=PIPE, stderr=PIPE)
output, error = process.communicate()
if error:
logger.error(f"{error=}")
return error.decode(), process.returncode
return output.decode(), process.returncode
def signal_alert(body: str, title: str = "") -> None:
"""Send a signal alert.
Args:
body (str): The body of the alert.
title (str, optional): The title of the alert. Defaults to "".
"""
apprise_client = Apprise()
from_phone = getenv("SIGNAL_ALERT_FROM_PHONE")
to_phone = getenv("SIGNAL_ALERT_TO_PHONE")
if not from_phone or not to_phone:
logger.info("SIGNAL_ALERT_FROM_PHONE or SIGNAL_ALERT_TO_PHONE not set")
return
apprise_client.add(f"signal://localhost:8989/{from_phone}/{to_phone}")
apprise_client.notify(title=title, body=body)
def utcnow() -> datetime:
"""Get the current UTC time."""
return datetime.now(tz=UTC)

59
python/database.py Normal file
View File

@@ -0,0 +1,59 @@
"""database."""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from sqlalchemy import inspect
from sqlalchemy.exc import NoInspectionAvailable
if TYPE_CHECKING:
from collections.abc import Sequence
from sqlalchemy.orm import Session
logger = logging.getLogger(__name__)
def safe_insert(orm_objects: Sequence[object], session: Session) -> list[tuple[Exception, object]]:
"""Safer insert at allows for partial rollbacks.
Args:
orm_objects (Sequence[object]): Tables to insert.
session (Session): Database session.
"""
if unmapped := [orm_object for orm_object in orm_objects if not _is_mapped_instance(orm_object)]:
error = f"safe_insert expects ORM-mapped instances {unmapped}"
raise TypeError(error)
return _safe_insert(orm_objects, session)
def _safe_insert(objects: Sequence[object], session: Session) -> list[tuple[Exception, object]]:
exceptions: list[tuple[Exception, object]] = []
try:
session.add_all(objects)
session.commit()
except Exception as error:
session.rollback()
objects_len = len(objects)
if objects_len == 1:
logger.exception(objects)
return [(error, objects[0])]
middle = objects_len // 2
exceptions.extend(_safe_insert(objects=objects[:middle], session=session))
exceptions.extend(_safe_insert(objects=objects[middle:], session=session))
return exceptions
def _is_mapped_instance(obj: object) -> bool:
"""Return True if `obj` is a SQLAlchemy ORM-mapped instance."""
try:
inspect(obj) # raises NoInspectionAvailable if not mapped
except NoInspectionAvailable:
return False
else:
return True

View File

@@ -0,0 +1 @@
"""installer."""

View File

@@ -0,0 +1,308 @@
"""Install NixOS on a ZFS pool."""
from __future__ import annotations
import curses
import logging
import sys
from os import getenv
from pathlib import Path
from random import getrandbits
from subprocess import PIPE, Popen, run
from time import sleep
from typing import TYPE_CHECKING
from python.common import configure_logger
from python.installer.tui import draw_menu
if TYPE_CHECKING:
from collections.abc import Sequence
logger = logging.getLogger(__name__)
def bash_wrapper(command: str) -> str:
"""Execute a bash command and capture the output.
Args:
command (str): The bash command to be executed.
Returns:
Tuple[str, int]: A tuple containing the output of the command (stdout) as a string,
the error output (stderr) as a string (optional), and the return code as an integer.
"""
logger.debug(f"running {command=}")
# This is a acceptable risk
process = Popen(command.split(), stdout=PIPE, stderr=PIPE)
output, _ = process.communicate()
if process.returncode != 0:
error = f"Failed to run command {command=} return code {process.returncode=}"
raise RuntimeError(error)
return output.decode()
def partition_disk(disk: str, swap_size: int, reserve: int = 0) -> None:
"""Partition a disk.
Args:
disk (str): The disk to partition.
swap_size (int): The size of the swap partition in GB.
minimum value is 1.
reserve (int, optional): The size of the reserve partition in GB. Defaults to 0.
minimum value is 0.
"""
logger.info(f"partitioning {disk=}")
swap_size = max(swap_size, 1)
reserve = max(reserve, 0)
bash_wrapper(f"blkdiscard -f {disk}")
if reserve > 0:
msg = f"Creating swap partition on {disk=} with size {swap_size=}GiB and reserve {reserve=}GiB"
logger.info(msg)
swap_start = swap_size + reserve
swap_partition = f"mkpart swap -{swap_start}GiB -{reserve}GiB "
else:
logger.info(f"Creating swap partition on {disk=} with size {swap_size=}GiB")
swap_start = swap_size
swap_partition = f"mkpart swap -{swap_start}GiB 100% "
logger.debug(f"{swap_partition=}")
create_partitions = (
f"parted --script --align=optimal {disk} -- "
"mklabel gpt "
"mkpart EFI 1MiB 4GiB "
f"mkpart root_pool 4GiB -{swap_start}GiB "
f"{swap_partition}"
"set 1 esp on"
)
bash_wrapper(create_partitions)
logger.info(f"{disk=} successfully partitioned")
def create_zfs_pool(pool_disks: Sequence[str], mnt_dir: str) -> None:
"""Create a ZFS pool.
Args:
pool_disks (Sequence[str]): A tuple of disks to use for the pool.
mnt_dir (str): The mount directory.
"""
if len(pool_disks) <= 0:
error = "disks must be a tuple of at least length 1"
raise ValueError(error)
zpool_create = (
"zpool create "
"-o ashift=12 "
"-o autotrim=on "
f"-R {mnt_dir} "
"-O acltype=posixacl "
"-O canmount=off "
"-O dnodesize=auto "
"-O normalization=formD "
"-O relatime=on "
"-O xattr=sa "
"-O mountpoint=legacy "
"-O compression=zstd "
"-O atime=off "
"root_pool "
)
if len(pool_disks) == 1:
zpool_create += pool_disks[0]
else:
zpool_create += "mirror "
zpool_create += " ".join(pool_disks)
bash_wrapper(zpool_create)
zpools = bash_wrapper("zpool list -o name")
if "root_pool" not in zpools.splitlines():
logger.critical("Failed to create root_pool")
sys.exit(1)
def create_zfs_datasets() -> None:
"""Create ZFS datasets."""
bash_wrapper("zfs create -o canmount=noauto -o reservation=10G root_pool/root")
bash_wrapper("zfs create root_pool/home")
bash_wrapper("zfs create root_pool/var -o reservation=1G")
bash_wrapper("zfs create -o compression=zstd-9 -o reservation=10G root_pool/nix")
datasets = bash_wrapper("zfs list -o name")
expected_datasets = {
"root_pool/root",
"root_pool/home",
"root_pool/var",
"root_pool/nix",
}
missing_datasets = expected_datasets.difference(datasets.splitlines())
if missing_datasets:
logger.critical(f"Failed to create pools {missing_datasets}")
sys.exit(1)
def get_cpu_manufacturer() -> str:
"""Get the CPU manufacturer."""
output = bash_wrapper("cat /proc/cpuinfo")
id_vendor = {"AuthenticAMD": "amd", "GenuineIntel": "intel"}
for line in output.splitlines():
if "vendor_id" in line:
return id_vendor[line.split(": ")[1].strip()]
error = "Failed to get CPU manufacturer"
raise RuntimeError(error)
def get_boot_drive_id(disk: str) -> str:
"""Get the boot drive ID."""
output = bash_wrapper(f"lsblk -o UUID {disk}-part1")
return output.splitlines()[1]
def create_nix_hardware_file(mnt_dir: str, disks: Sequence[str], encrypt: str | None) -> None:
"""Create a NixOS hardware file."""
cpu_manufacturer = get_cpu_manufacturer()
devices = ""
if encrypt:
disk = disks[0]
devices = (
f' luks.devices."luks-root-pool-{disk.split("/")[-1]}-part2"'
"= {\n"
f' device = "{disk}-part2";\n'
" bypassWorkqueues = true;\n"
" allowDiscards = true;\n"
" };\n"
)
host_id = format(getrandbits(32), "08x")
nix_hardware = (
"{ config, lib, modulesPath, ... }:\n"
"{\n"
' imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];\n\n'
" boot = {\n"
" initrd = {\n"
' availableKernelModules = [ \n "ahci"\n "ehci_pci"\n "nvme"\n "sd_mod"\n'
' "usb_storage"\n "usbhid"\n "xhci_pci"\n ];\n'
" kernelModules = [ ];\n"
f" {devices}"
" };\n"
f' kernelModules = [ "kvm-{cpu_manufacturer}" ];\n'
" extraModulePackages = [ ];\n"
" };\n\n"
" fileSystems = {\n"
' "/" = lib.mkDefault {\n device = "root_pool/root";\n fsType = "zfs";\n };\n\n'
' "/home" = {\n device = "root_pool/home";\n fsType = "zfs";\n };\n\n'
' "/var" = {\n device = "root_pool/var";\n fsType = "zfs";\n };\n\n'
' "/nix" = {\n device = "root_pool/nix";\n fsType = "zfs";\n };\n\n'
' "/boot" = {\n'
f' device = "/dev/disk/by-uuid/{get_boot_drive_id(disks[0])}";\n'
' fsType = "vfat";\n options = [\n "fmask=0077"\n'
' "dmask=0077"\n ];\n };\n };\n\n'
" swapDevices = [ ];\n\n"
" networking.useDHCP = lib.mkDefault true;\n\n"
' nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";\n'
f" hardware.cpu.{cpu_manufacturer}.updateMicrocode = "
"lib.mkDefault config.hardware.enableRedistributableFirmware;\n"
f' networking.hostId = "{host_id}";\n'
"}\n"
)
Path(f"{mnt_dir}/etc/nixos/hardware-configuration.nix").write_text(nix_hardware)
def install_nixos(mnt_dir: str, disks: Sequence[str], encrypt: str | None) -> None:
"""Install NixOS."""
bash_wrapper(f"mount -o X-mount.mkdir -t zfs root_pool/root {mnt_dir}")
bash_wrapper(f"mount -o X-mount.mkdir -t zfs root_pool/home {mnt_dir}/home")
bash_wrapper(f"mount -o X-mount.mkdir -t zfs root_pool/var {mnt_dir}/var")
bash_wrapper(f"mount -o X-mount.mkdir -t zfs root_pool/nix {mnt_dir}/nix")
for disk in disks:
bash_wrapper(f"mkfs.vfat -n EFI {disk}-part1")
# set up mirroring afterwards if more than one disk
boot_partition = (
f"mount -t vfat -o fmask=0077,dmask=0077,iocharset=iso8859-1,X-mount.mkdir {disks[0]}-part1 {mnt_dir}/boot"
)
bash_wrapper(boot_partition)
bash_wrapper(f"nixos-generate-config --root {mnt_dir}")
create_nix_hardware_file(mnt_dir, disks, encrypt)
run(("nixos-install", "--root", mnt_dir), check=True)
def installer(
disks: Sequence[str],
swap_size: int,
reserve: int,
encrypt_key: str | None,
) -> None:
"""Main."""
logger.info("Starting installation")
for disk in disks:
partition_disk(disk, swap_size, reserve)
test = Popen(("printf", f"'{encrypt_key}'"), stdout=PIPE)
if encrypt_key:
sleep(1)
for command in (
f"cryptsetup luksFormat --type luks2 {disk}-part2 -",
f"cryptsetup luksOpen {disk}-part2 luks-root-pool-{disk.split('/')[-1]}-part2 -",
):
run(command, check=True, stdin=test.stdout)
mnt_dir = "/tmp/nix_install" # noqa: S108
Path(mnt_dir).mkdir(parents=True, exist_ok=True)
if encrypt_key:
pool_disks = [f"/dev/mapper/luks-root-pool-{disk.split('/')[-1]}-part2" for disk in disks]
else:
pool_disks = [f"{disk}-part2" for disk in disks]
create_zfs_pool(pool_disks, mnt_dir)
create_zfs_datasets()
install_nixos(mnt_dir, disks, encrypt_key)
logger.info("Installation complete")
def main() -> None:
"""Main."""
configure_logger("DEBUG")
state = curses.wrapper(draw_menu)
encrypt_key = getenv("ENCRYPT_KEY")
logger.info("installing_nixos")
logger.info(f"disks: {state.selected_device_ids}")
logger.info(f"swap_size: {state.swap_size}")
logger.info(f"reserve: {state.reserve_size}")
logger.info(f"encrypted: {bool(encrypt_key)}")
sleep(3)
installer(
disks=state.get_selected_devices(),
swap_size=state.swap_size,
reserve=state.reserve_size,
encrypt_key=encrypt_key,
)
if __name__ == "__main__":
main()

498
python/installer/tui.py Normal file
View File

@@ -0,0 +1,498 @@
"""TUI module."""
from __future__ import annotations
import curses
import logging
from collections import defaultdict
from subprocess import PIPE, Popen
logger = logging.getLogger(__name__)
def bash_wrapper(command: str) -> str:
"""Execute a bash command and capture the output.
Args:
command (str): The bash command to be executed.
Returns:
Tuple[str, int]: A tuple containing the output of the command (stdout) as a string,
the error output (stderr) as a string (optional), and the return code as an integer.
"""
logger.debug(f"running {command=}")
# This is a acceptable risk
process = Popen(command.split(), stdout=PIPE, stderr=PIPE)
output, _ = process.communicate()
if process.returncode != 0:
error = f"Failed to run command {command=} return code {process.returncode=}"
raise RuntimeError(error)
return output.decode()
class Cursor:
"""Cursor class."""
def __init__(self) -> None:
"""Initialize the Cursor class."""
self.x_position = 0
self.y_position = 0
self.height = 0
self.width = 0
def set_height(self, height: int) -> None:
"""Set height."""
self.height = height
def set_width(self, width: int) -> None:
"""Set width."""
self.width = width
def x_bounce_check(self, cursor: int) -> int:
"""X bounce check."""
cursor = max(0, cursor)
return min(self.width - 1, cursor)
def y_bounce_check(self, cursor: int) -> int:
"""Y bounce check."""
cursor = max(0, cursor)
return min(self.height - 1, cursor)
def set_x(self, x: int) -> None:
"""Set x."""
self.x_position = self.x_bounce_check(x)
def set_y(self, y: int) -> None:
"""Set y."""
self.y_position = self.y_bounce_check(y)
def get_x(self) -> int:
"""Get x."""
return self.x_position
def get_y(self) -> int:
"""Get y."""
return self.y_position
def move_up(self) -> None:
"""Move up."""
self.set_y(self.y_position - 1)
def move_down(self) -> None:
"""Move down."""
self.set_y(self.y_position + 1)
def move_left(self) -> None:
"""Move left."""
self.set_x(self.x_position - 1)
def move_right(self) -> None:
"""Move right."""
self.set_x(self.x_position + 1)
def navigation(self, key: int) -> None:
"""Navigation.
Args:
key (int): The key.
"""
action = {
curses.KEY_DOWN: self.move_down,
curses.KEY_UP: self.move_up,
curses.KEY_RIGHT: self.move_right,
curses.KEY_LEFT: self.move_left,
}
action.get(key, lambda: None)()
class State:
"""State class to store the state of the program."""
def __init__(self) -> None:
"""Initialize the State class."""
self.key = 0
self.cursor = Cursor()
self.swap_size = 0
self.show_swap_input = False
self.reserve_size = 0
self.show_reserve_input = False
self.selected_device_ids: set[str] = set()
def get_selected_devices(self) -> tuple[str, ...]:
"""Get selected devices."""
return tuple(self.selected_device_ids)
def get_device(raw_device: str) -> dict[str, str]:
"""Get a device.
Args:
raw_device (str): The raw device.
Returns:
dict[str, str]: The device.
"""
raw_device_components = raw_device.split(" ")
return {thing.split("=")[0].lower(): thing.split("=")[1].strip('"') for thing in raw_device_components}
def get_devices() -> list[dict[str, str]]:
"""Get a list of devices."""
# --bytes
raw_devices = bash_wrapper("lsblk --paths --pairs").splitlines()
return [get_device(raw_device) for raw_device in raw_devices]
def set_color() -> None:
"""Set the color."""
curses.start_color()
curses.use_default_colors()
for i in range(curses.COLORS):
curses.init_pair(i + 1, i, -1)
def debug_menu(std_screen: curses.window, key: int) -> None:
"""Debug menu.
Args:
std_screen (curses.window): The curses window.
key (int): The key.
"""
height, width = std_screen.getmaxyx()
std_screen.addstr(height - 4, 0, f"Width: {width}, Height: {height}", curses.color_pair(5))
key_pressed = f"Last key pressed: {key}"[: width - 1]
if key == 0:
key_pressed = "No key press detected..."[: width - 1]
std_screen.addstr(height - 3, 0, key_pressed)
for i in range(8):
std_screen.addstr(height - 2, i * 3, f"{i}██", curses.color_pair(i))
def get_text_input(std_screen: curses.window, prompt: str, y: int, x: int) -> str:
"""Get text input.
Args:
std_screen (curses.window): The curses window.
prompt (str): The prompt.
y (int): The y position.
x (int): The x position.
Returns:
str: The input string.
"""
esc_key = 27
curses.echo()
std_screen.addstr(y, x, prompt)
input_str = ""
while True:
key = std_screen.getch()
if key == ord("\n"):
break
if key == esc_key:
input_str = ""
break
if key in (curses.KEY_BACKSPACE, ord("\b"), 127):
input_str = input_str[:-1]
std_screen.addstr(y, x + len(prompt), input_str + " ")
else:
input_str += chr(key)
std_screen.refresh()
curses.noecho()
return input_str
def swap_size_input(
std_screen: curses.window,
state: State,
swap_offset: int,
) -> State:
"""Reserve size input.
Args:
std_screen (curses.window): The curses window.
state (State): The state object.
swap_offset (int): The swap offset.
Returns:
State: The updated state object.
"""
swap_size_text = "Swap size (GB): "
std_screen.addstr(swap_offset, 0, f"{swap_size_text}{state.swap_size}")
if state.key == ord("\n") and state.cursor.get_y() == swap_offset:
state.show_swap_input = True
if state.show_swap_input:
swap_size_str = get_text_input(std_screen, swap_size_text, swap_offset, 0)
try:
state.swap_size = int(swap_size_str)
state.show_swap_input = False
except ValueError:
std_screen.addstr(swap_offset, 0, "Invalid input. Press any key to continue.")
std_screen.getch()
state.show_swap_input = False
return state
def reserve_size_input(
std_screen: curses.window,
state: State,
reserve_offset: int,
) -> State:
"""Reserve size input.
Args:
std_screen (curses.window): The curses window.
state (State): The state object.
reserve_offset (int): The reserve offset.
Returns:
State: The updated state object.
"""
reserve_size_text = "reserve size (GB): "
std_screen.addstr(reserve_offset, 0, f"{reserve_size_text}{state.reserve_size}")
if state.key == ord("\n") and state.cursor.get_y() == reserve_offset:
state.show_reserve_input = True
if state.show_reserve_input:
reserve_size_str = get_text_input(std_screen, reserve_size_text, reserve_offset, 0)
try:
state.reserve_size = int(reserve_size_str)
state.show_reserve_input = False
except ValueError:
std_screen.addstr(reserve_offset, 0, "Invalid input. Press any key to continue.")
std_screen.getch()
state.show_reserve_input = False
return state
def status_bar(
std_screen: curses.window,
cursor: Cursor,
width: int,
height: int,
) -> None:
"""Draw the status bar.
Args:
std_screen (curses.window): The curses window.
cursor (Cursor): The cursor.
width (int): The width.
height (int): The height.
"""
std_screen.attron(curses.A_REVERSE)
std_screen.attron(curses.color_pair(3))
status_bar = f"Press 'q' to exit | STATUS BAR | Pos: {cursor.get_x()}, {cursor.get_y()}"
std_screen.addstr(height - 1, 0, status_bar)
std_screen.addstr(height - 1, len(status_bar), " " * (width - len(status_bar) - 1))
std_screen.attroff(curses.color_pair(3))
std_screen.attroff(curses.A_REVERSE)
def get_device_id_mapping() -> dict[str, set[str]]:
"""Get a list of device ids.
Returns:
list[str]: the list of device ids
"""
device_ids = bash_wrapper("find /dev/disk/by-id -type l").splitlines()
device_id_mapping: dict[str, set[str]] = defaultdict(set)
for device_id in device_ids:
device = bash_wrapper(f"readlink -f {device_id}").strip()
device_id_mapping[device].add(device_id)
return device_id_mapping
def calculate_device_menu_padding(devices: list[dict[str, str]], column: str, padding: int = 0) -> int:
"""Calculate the device menu padding.
Args:
devices (list[dict[str, str]]): The devices.
column (str): The column.
padding (int, optional): The padding. Defaults to 0.
Returns:
int: The calculated padding.
"""
return max(len(device[column]) for device in devices) + padding
def draw_device_ids(
state: State,
row_number: int,
menu_start_x: int,
std_screen: curses.window,
menu_width: list[int],
device_ids: set[str],
) -> tuple[State, int]:
"""Draw device IDs.
Args:
state (State): The state object.
row_number (int): The row number.
menu_start_x (int): The menu start x.
std_screen (curses.window): The curses window.
menu_width (list[int]): The menu width.
device_ids (set[str]): The device IDs.
Returns:
tuple[State, int]: The updated state object and the row number.
"""
for device_id in sorted(device_ids):
row_number = row_number + 1
if row_number == state.cursor.get_y() and state.cursor.get_x() in menu_width:
std_screen.attron(curses.A_BOLD)
if state.key == ord(" "):
if device_id not in state.selected_device_ids:
state.selected_device_ids.add(device_id)
else:
state.selected_device_ids.remove(device_id)
if device_id in state.selected_device_ids:
std_screen.attron(curses.color_pair(7))
std_screen.addstr(row_number, menu_start_x, f" {device_id}")
std_screen.attroff(curses.color_pair(7))
std_screen.attroff(curses.A_BOLD)
return state, row_number
def draw_device_menu(
std_screen: curses.window,
devices: list[dict[str, str]],
device_id_mapping: dict[str, set[str]],
state: State,
menu_start_y: int = 0,
menu_start_x: int = 0,
) -> tuple[State, int]:
"""Draw the device menu and handle user input.
Args:
std_screen (curses.window): the curses window to draw on
devices (list[dict[str, str]]): the list of devices to draw
device_id_mapping (dict[str, set[str]]): the list of device ids to draw
state (State): the state object to update
menu_start_y (int, optional): the y position to start drawing the menu. Defaults to 0.
menu_start_x (int, optional): the x position to start drawing the menu. Defaults to 0.
Returns:
State: the updated state object
"""
padding = 2
name_padding = calculate_device_menu_padding(devices, "name", padding)
size_padding = calculate_device_menu_padding(devices, "size", padding)
type_padding = calculate_device_menu_padding(devices, "type", padding)
mountpoints_padding = calculate_device_menu_padding(devices, "mountpoints", padding)
device_header = (
f"{'Name':{name_padding}}{'Size':{size_padding}}{'Type':{type_padding}}{'Mountpoints':{mountpoints_padding}}"
)
menu_width = list(range(menu_start_x, len(device_header) + menu_start_x))
std_screen.addstr(menu_start_y, menu_start_x, device_header, curses.color_pair(5))
devises_list_start = menu_start_y + 1
row_number = devises_list_start
for device in devices:
row_number = row_number + 1
device_name = device["name"]
device_row = (
f"{device_name:{name_padding}}"
f"{device['size']:{size_padding}}"
f"{device['type']:{type_padding}}"
f"{device['mountpoints']:{mountpoints_padding}}"
)
std_screen.addstr(row_number, menu_start_x, device_row)
state, row_number = draw_device_ids(
state=state,
row_number=row_number,
menu_start_x=menu_start_x,
std_screen=std_screen,
menu_width=menu_width,
device_ids=device_id_mapping[device_name],
)
return state, row_number
def draw_menu(std_screen: curses.window) -> State:
"""Draw the menu and handle user input.
Args:
std_screen (curses.window): the curses window to draw on
Returns:
State: the state object
"""
# Clear and refresh the screen for a blank canvas
std_screen.clear()
std_screen.refresh()
set_color()
state = State()
devices = get_devices()
device_id_mapping = get_device_id_mapping()
# Loop where k is the last character pressed
while state.key != ord("q"):
std_screen.clear()
height, width = std_screen.getmaxyx()
state.cursor.set_height(height)
state.cursor.set_width(width)
state.cursor.navigation(state.key)
state, device_menu_size = draw_device_menu(
std_screen=std_screen,
state=state,
devices=devices,
device_id_mapping=device_id_mapping,
)
swap_offset = device_menu_size + 2
swap_size_input(
std_screen=std_screen,
state=state,
swap_offset=swap_offset,
)
reserve_size_input(
std_screen=std_screen,
state=state,
reserve_offset=swap_offset + 1,
)
status_bar(std_screen, state.cursor, width, height)
debug_menu(std_screen, state.key)
std_screen.move(state.cursor.get_y(), state.cursor.get_x())
std_screen.refresh()
state.key = std_screen.getch()
return state

155
python/parallelize.py Normal file
View File

@@ -0,0 +1,155 @@
"""Thing."""
from __future__ import annotations
import logging
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
from dataclasses import dataclass
from multiprocessing import cpu_count
from typing import TYPE_CHECKING, Any, Literal, TypeVar
if TYPE_CHECKING:
from collections.abc import Callable, Mapping, Sequence
logger = logging.getLogger(__name__)
R = TypeVar("R")
modes = Literal["normal", "early_error"]
@dataclass
class ExecutorResults[R]:
"""Dataclass to store the results and exceptions of the parallel execution."""
results: list[R]
exceptions: list[BaseException]
def __repr__(self) -> str:
"""Return a string representation of the object."""
return f"results={self.results} exceptions={self.exceptions}"
def _parallelize_base[R](
executor_type: type[ThreadPoolExecutor | ProcessPoolExecutor],
func: Callable[..., R],
kwargs_list: Sequence[Mapping[str, Any]],
max_workers: int | None,
progress_tracker: int | None,
mode: modes,
) -> ExecutorResults:
total_work = len(kwargs_list)
with executor_type(max_workers=max_workers) as executor:
futures = [executor.submit(func, **kwarg) for kwarg in kwargs_list]
results = []
exceptions = []
for index, future in enumerate(futures, 1):
if exception := future.exception():
logger.error(f"{future} raised {exception.__class__.__name__}")
exceptions.append(exception)
if mode == "early_error":
executor.shutdown(wait=False)
raise exception
continue
results.append(future.result())
if progress_tracker and index % progress_tracker == 0:
logger.info(f"Progress: {index}/{total_work}")
return ExecutorResults(results, exceptions)
def parallelize_thread[R](
func: Callable[..., R],
kwargs_list: Sequence[Mapping[str, Any]],
max_workers: int | None = None,
progress_tracker: int | None = None,
mode: modes = "normal",
) -> ExecutorResults:
"""Generic function to run a function with multiple arguments in threads.
Args:
func (Callable[..., R]): Function to run in threads.
kwargs_list (Sequence[Mapping[str, Any]]): List of dictionaries with the arguments for the function.
max_workers (int, optional): Number of workers to use. Defaults to 8.
progress_tracker (int, optional): Number of tasks to complete before logging progress.
mode (modes, optional): Mode to use. Defaults to "normal".
Returns:
tuple[list[R], list[Exception]]: List with the results and a list with the exceptions.
"""
return _parallelize_base(
executor_type=ThreadPoolExecutor,
func=func,
kwargs_list=kwargs_list,
max_workers=max_workers,
progress_tracker=progress_tracker,
mode=mode,
)
def parallelize_process[R](
func: Callable[..., R],
kwargs_list: Sequence[Mapping[str, Any]],
max_workers: int | None = None,
progress_tracker: int | None = None,
mode: modes = "normal",
) -> ExecutorResults:
"""Generic function to run a function with multiple arguments in process.
Args:
func (Callable[..., R]): Function to run in process.
kwargs_list (Sequence[Mapping[str, Any]]): List of dictionaries with the arguments for the function.
max_workers (int, optional): Number of workers to use. Defaults to 4.
progress_tracker (int, optional): Number of tasks to complete before logging progress.
mode (modes, optional): Mode to use. Defaults to "normal".
Returns:
tuple[list[R], list[Exception]]: List with the results and a list with the exceptions.
"""
if max_workers and max_workers > cpu_count():
error = f"max_workers must be less than or equal to {cpu_count()}"
raise RuntimeError(error)
return process_executor_unchecked(
func=func,
kwargs_list=kwargs_list,
max_workers=max_workers,
progress_tracker=progress_tracker,
mode=mode,
)
def process_executor_unchecked[R](
func: Callable[..., R],
kwargs_list: Sequence[Mapping[str, Any]],
max_workers: int | None,
progress_tracker: int | None,
mode: modes = "normal",
) -> ExecutorResults:
"""Generic function to run a function with multiple arguments in parallel.
Note: this function does not check if the number of workers is greater than the number of CPUs.
This can cause the system to become unresponsive.
Args:
func (Callable[..., R]): Function to run in parallel.
kwargs_list (Sequence[Mapping[str, Any]]): List of dictionaries with the arguments for the function.
max_workers (int, optional): Number of workers to use. Defaults to 8.
progress_tracker (int, optional): Number of tasks to complete before logging progress.
mode (modes, optional): Mode to use. Defaults to "normal".
Returns:
tuple[list[R], list[Exception]]: List with the results and a list with the exceptions.
"""
return _parallelize_base(
executor_type=ProcessPoolExecutor,
func=func,
kwargs_list=kwargs_list,
max_workers=max_workers,
progress_tracker=progress_tracker,
mode=mode,
)

View File

@@ -0,0 +1 @@
"""init."""

View File

@@ -0,0 +1,40 @@
"""capasitor."""
def calculate_capacitor_capacity(voltage: float, farads: float) -> float:
"""Calculate capacitor capacity."""
joules = (farads * voltage**2) // 2
return joules // 3600
def calculate_pack_capacity(cells: int, cell_voltage: float, farads: float) -> float:
"""Calculate pack capacity."""
return calculate_capacitor_capacity(cells * cell_voltage, farads / cells)
def calculate_pack_capacity2(cells: int, cell_voltage: float, farads: float, cell_cost: float) -> tuple[float, float]:
"""Calculate pack capacity."""
capacitor_capacity = calculate_capacitor_capacity(cells * cell_voltage, farads / cells)
return capacitor_capacity, cell_cost * cells
def main() -> None:
"""Main."""
watt_hours = calculate_pack_capacity(cells=10, cell_voltage=2.7, farads=500)
print(f"{watt_hours=}")
print(f"{watt_hours*16=}")
watt_hours = calculate_pack_capacity(cells=1, cell_voltage=2.7, farads=5000)
print(f"{watt_hours=}")
watt_hours, cost = calculate_pack_capacity2(
cells=10,
cell_voltage=2.7,
farads=3000,
cell_cost=11.60,
)
print(f"{watt_hours=}")
print(f"{cost=}")
if __name__ == "__main__":
main()

25
python/random/thing.py Normal file
View File

@@ -0,0 +1,25 @@
"""thing."""
def caculat_batry_specs(
cell_amp_hour: int,
cell_voltage: float,
cells_per_pack: int,
packs: int,
) -> tuple[float, float]:
"""Caculat battry specs."""
pack_voltage = cell_voltage * cells_per_pack
pack_watt_hours = pack_voltage * cell_amp_hour
battry_capacity = pack_watt_hours * packs
return (
battry_capacity,
pack_voltage,
)
battry_capacity, pack_voltage = caculat_batry_specs(300, 3.2, 8, 2)
print(f"{battry_capacity=} {pack_voltage=}")
cost = 1700
print(f"$/kWh {cost / battry_capacity}")

View File

@@ -0,0 +1,196 @@
"""voltage_drop."""
import math
from enum import Enum
class TemperatureUnit(Enum):
"""Temperature unit."""
CELSIUS = "c"
FAHRENHEIT = "f"
KELVIN = "k"
class Temperature:
"""Temperature."""
def __init__(
self,
temperature: float,
unit: TemperatureUnit = TemperatureUnit.CELSIUS,
) -> None:
"""__init__."""
unit_modifier = {
TemperatureUnit.CELSIUS: 1,
TemperatureUnit.FAHRENHEIT: 0.5556,
TemperatureUnit.KELVIN: 1.8,
}
self.temperature = temperature * unit_modifier[unit]
def __float__(self) -> float:
"""Return the temperature in degrees Celsius."""
return self.temperature
class LengthUnit(Enum):
"""Length unit."""
METERS = "m"
FEET = "ft"
INCHES = "in"
class Length:
"""Length."""
def __init__(self, length: float, unit: LengthUnit) -> None:
"""__init__."""
self.meters = self._convert_to_meters(length, unit)
def _convert_to_meters(self, length: float, unit: LengthUnit) -> float:
thing = {
LengthUnit.METERS: 1,
LengthUnit.FEET: 0.3048,
LengthUnit.INCHES: 0.0254,
}
test = thing.get(unit)
if test:
return length * test
error = f"Unsupported unit: {unit}"
raise ValueError(error)
def __float__(self) -> float:
"""Return the length in meters."""
return self.meters
def feet(self) -> float:
"""Return the length in feet."""
return self.meters * 3.2808
class MaterialType(Enum):
"""Material type."""
COPPER = "copper"
ALUMINUM = "aluminum"
CCA = "cca"
SILVER = "silver"
GOLD = "gold"
def get_material_resistivity(
material: MaterialType,
temperature: Temperature | None = None,
) -> float:
"""Get the resistivity of a material."""
if not temperature:
temperature = Temperature(20.0)
material_info = {
MaterialType.COPPER: (1.724e-8, 0.00393),
MaterialType.ALUMINUM: (2.908e-8, 0.00403),
MaterialType.CCA: (2.577e-8, 0.00397),
MaterialType.SILVER: (1.632e-8, 0.00380),
MaterialType.GOLD: (2.503e-8, 0.00340),
}
base_resistivity, temp_coefficient = material_info[material]
return base_resistivity * (1 + temp_coefficient * float(temperature))
def calculate_awg_diameter_mm(gauge: int) -> float:
"""Calculate wire diameter in millimeters for a given AWG gauge."""
return round(0.127 * 92 ** ((36 - gauge) / 39), 3)
def calculate_wire_area_m2(gauge: int) -> float:
"""Calculate the area of a wire in square meters.
Args:
gauge (int): The AWG (American Wire Gauge) number of the wire
Returns:
float: The area of the wire in square meters
"""
return math.pi * (calculate_awg_diameter_mm(gauge) / 2000) ** 2
def calculate_resistance_per_meter(gauge: int) -> float:
"""Calculate the resistance per meter of a wire.
Args:
gauge (int): The AWG (American Wire Gauge) number of the wire
Returns:
float: The resistance per meter of the wire
"""
return get_material_resistivity(MaterialType.COPPER) / calculate_wire_area_m2(gauge)
def voltage_drop(
gauge: int,
material: MaterialType,
length: Length,
current_a: float,
) -> float:
"""Calculate the voltage drop of a wire.
Args:
gauge (int): The AWG (American Wire Gauge) number of the wire
material (MaterialType): The type of conductor material (e.g., copper, aluminum)
length (Length): The length of the wire in meters
current_a (float): The current flowing through the wire in amperes
Returns:
float: The voltage drop of the wire in volts
"""
resistivity = get_material_resistivity(material)
resistance_per_meter = resistivity / calculate_wire_area_m2(gauge)
total_resistance = resistance_per_meter * float(length) * 2 # round-trip
return total_resistance * current_a
print(
voltage_drop(
gauge=10,
material=MaterialType.CCA,
length=Length(length=20, unit=LengthUnit.FEET),
current_a=20,
)
)
def max_wire_length(
gauge: int,
material: MaterialType,
current_amps: float,
voltage_drop: float = 0.3,
temperature: Temperature | None = None,
) -> Length:
"""Calculate the maximum allowable wire length based on voltage drop criteria.
Args:
gauge (int): The AWG (American Wire Gauge) number of the wire
material (MaterialType): The type of conductor material (e.g., copper, aluminum)
current_amps (float): The current flowing through the wire in amperes
voltage_drop (float, optional): Maximum allowable voltage drop as a decimal (default 0.1 or 10%)
temperature (Temperature | None, optional): The temperature of the wire. Defaults to None.
Returns:
float: Maximum wire length in meters that maintains the specified voltage drop
"""
if not temperature:
temperature = Temperature(100.0, unit=TemperatureUnit.FAHRENHEIT)
resistivity = get_material_resistivity(material, temperature)
resistance_per_meter = resistivity / calculate_wire_area_m2(gauge)
# V = IR, solve for length where V is the allowed voltage drop
return Length(
voltage_drop / (current_amps * resistance_per_meter),
LengthUnit.METERS,
)
print(max_wire_length(gauge=10, material=MaterialType.CCA, current_amps=20).feet())
print(max_wire_length(gauge=10, material=MaterialType.CCA, current_amps=10).feet())
print(max_wire_length(gauge=10, material=MaterialType.CCA, current_amps=5).feet())

View File

@@ -0,0 +1 @@
"""system_tests."""

View File

@@ -0,0 +1,99 @@
"""Validate Jeeves."""
from __future__ import annotations
import logging
from copy import copy
from re import search
from time import sleep
from typing import TYPE_CHECKING
from python.common import bash_wrapper
from python.zfs import Zpool
if TYPE_CHECKING:
from collections.abc import Sequence
logger = logging.getLogger(__name__)
def zpool_tests(pool_names: Sequence[str], zpool_capacity_threshold: int = 90) -> list[str] | None:
"""Check the zpool health and capacity.
Args:
pool_names (Sequence[str]): A list of pool names to test.
zpool_capacity_threshold (int, optional): The threshold for the zpool capacity. Defaults to 90.
Returns:
list[str] | None: A list of errors if any.
"""
logger.info("Testing zpool")
errors: list[str] = []
for pool_name in pool_names:
pool = Zpool(pool_name)
if pool.health != "ONLINE":
errors.append(f"{pool.name} is {pool.health}")
if pool.capacity >= zpool_capacity_threshold:
errors.append(f"{pool.name} is low on space")
upgrade_status, _ = bash_wrapper("zpool upgrade")
if not search(r"Every feature flags pool has all supported and requested features enabled.", upgrade_status):
errors.append("ZPool out of date run `sudo zpool upgrade -a`")
return errors
def systemd_tests(
service_names: Sequence[str],
max_retries: int = 30,
retry_delay_secs: int = 1,
retryable_statuses: Sequence[str] | None = None,
valid_statuses: Sequence[str] | None = None,
) -> list[str] | None:
"""Tests a systemd services.
Args:
service_names (Sequence[str]): A list of service names to test.
max_retries (int, optional): The maximum number of retries. Defaults to 30.
minimum value is 1.
retry_delay_secs (int, optional): The delay between retries in seconds. Defaults to 1.
minimum value is 1.
retryable_statuses (Sequence[str] | None, optional): A list of retryable statuses. Defaults to None.
valid_statuses (Sequence[str] | None, optional): A list of valid statuses. Defaults to None.
Returns:
list[str] | None: A list of errors if any.
"""
logger.info("Testing systemd service")
max_retries = max(max_retries, 1)
retry_delay_secs = max(retry_delay_secs, 1)
last_try = max_retries - 1
if retryable_statuses is None:
retryable_statuses = ("inactive\n", "activating\n")
if valid_statuses is None:
valid_statuses = ("active\n",)
service_names_set = set(service_names)
errors: set[str] = set()
for retry in range(max_retries):
if not service_names_set:
break
logger.info(f"Testing systemd service in {retry + 1} of {max_retries}")
service_names_to_test = copy(service_names_set)
for service_name in service_names_to_test:
service_status, _ = bash_wrapper(f"systemctl is-active {service_name}")
if service_status in valid_statuses:
service_names_set.remove(service_name)
continue
if service_status in retryable_statuses and retry < last_try:
continue
errors.add(f"{service_name} is {service_status.strip()}")
sleep(retry_delay_secs)
return list(errors)

View File

@@ -0,0 +1,66 @@
"""Validate {server_name}."""
import logging
import sys
import tomllib
from os import environ
from pathlib import Path
from socket import gethostname
import typer
from python.common import configure_logger, signal_alert
from python.system_tests.components import systemd_tests, zpool_tests
logger = logging.getLogger(__name__)
def load_config_data(config_file: Path) -> dict[str, list[str]]:
"""Load a TOML configuration file.
Args:
config_file (Path): The path to the configuration file.
Returns:
dict: The configuration data.
"""
return tomllib.loads(config_file.read_text())
def main(config_file: Path) -> None:
"""Main."""
configure_logger(level=environ.get("LOG_LEVEL", "INFO"))
server_name = gethostname()
logger.info(f"Starting {server_name} validation")
config_data = load_config_data(config_file)
errors: list[str] = []
try:
if config_data.get("zpools") and (zpool_errors := zpool_tests(config_data["zpools"])):
errors.extend(zpool_errors)
if config_data.get("services") and (systemd_errors := systemd_tests(config_data["services"])):
errors.extend(systemd_errors)
except Exception as error:
logger.exception(f"{server_name} validation failed")
errors.append(f"{server_name} validation failed: {error}")
if errors:
logger.error(f"{server_name} validation failed: \n{'\n'.join(errors)}")
signal_alert(f"{server_name} validation failed {errors}")
sys.exit(1)
logger.info(f"{server_name} validation passed")
def cli() -> None:
"""CLI."""
typer.run(main)
if __name__ == "__main__":
cli()

View File

@@ -0,0 +1 @@
"""init."""

View File

@@ -0,0 +1 @@
"""init."""

View File

@@ -0,0 +1,11 @@
"""Bar."""
import logging
logger = logging.getLogger(__name__)
def bar() -> None:
"""Bar."""
logger.debug(f"bar {__name__}")
logger.debug("bar")

View File

@@ -0,0 +1,20 @@
"""configure_logger."""
import logging
import sys
def configure_logger(level: str = "INFO", test: str | None = None) -> None:
"""Configure the logger.
Args:
level (str, optional): The logging level. Defaults to "INFO".
test (str | None, optional): The test name. Defaults to None.
"""
logging.basicConfig(
level=level,
datefmt="%Y-%m-%dT%H:%M:%S%z",
format="%(asctime)s %(levelname)s %(filename)s:%(lineno)d - %(message)s" # this is nesiseary
f" {test}",
handlers=[logging.StreamHandler(sys.stdout)],
)

View File

@@ -0,0 +1,17 @@
"""foo."""
import logging
from python.testing.logging.bar import bar
from python.testing.logging.configure_logger import configure_logger
logger = logging.getLogger(__name__)
def foo() -> None:
"""Foo."""
configure_logger("DEBUG", "FOO")
logger.debug(f"foo {__name__}")
logger.debug("foo")
bar()

View File

@@ -0,0 +1,33 @@
"""main."""
import logging
from python.testing.logging.bar import bar
from python.testing.logging.configure_logger import configure_logger
from python.testing.logging.foo import foo
logger = logging.getLogger(__name__)
def main() -> None:
"""Main."""
configure_logger("DEBUG")
# handler = logging.StreamHandler()
# Create and attach a formatter
# formatter = logging.Formatter(
# "%(asctime)s %(levelname)s %(filename)s:%(lineno)d - %(message)s FOO"
# )
# handler.setFormatter(formatter)
# Attach handler to logger
# foo_logger = logging.getLogger("python.testing.logging.foo")
# foo_logger.addHandler(handler)
# foo_logger.propagate = True
logger.debug("main")
foo()
bar()
if __name__ == "__main__":
main()

1
python/tools/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Server Tools."""

View File

@@ -0,0 +1,144 @@
"""snapshot_manager."""
from __future__ import annotations
import logging
import sys
import tomllib
from functools import cache
from pathlib import Path # noqa: TC003 This is required for the typer CLI
from re import compile as re_compile
from re import search
import typer
from python.common import configure_logger, signal_alert, utcnow
from python.zfs import Dataset, get_datasets
logger = logging.getLogger(__name__)
def main(config_file: Path) -> None:
"""Main."""
configure_logger(level="DEBUG")
logger.info("Starting snapshot_manager")
try:
time_stamp = get_time_stamp()
for dataset in get_datasets():
status = dataset.create_snapshot(time_stamp)
logger.debug(f"{status=}")
if status != "snapshot created":
msg = f"{dataset.name} failed to create snapshot {time_stamp}"
logger.error(msg)
signal_alert(msg)
continue
get_snapshots_to_delete(dataset, get_count_lookup(config_file, dataset.name))
except Exception:
logger.exception("snapshot_manager failed")
signal_alert("snapshot_manager failed")
sys.exit(1)
else:
logger.info("snapshot_manager completed")
def get_count_lookup(config_file: Path, dataset_name: str) -> dict[str, int]:
"""Get the count lookup.
Args:
config_file (Path): The path to the configuration file.
dataset_name (str): The name of the dataset.
Returns:
dict[str, int]: The count lookup.
"""
config_data = load_config_data(config_file)
return config_data.get(dataset_name, get_default_config(config_data))
def get_default_config(config_data: dict[str, dict[str, int]]) -> dict[str, int]:
"""Get the default configuration.
Args:
config_data (dict[str, dict[str, int]]): The configuration data.
Returns:
dict[str, int]: The default configuration.
"""
return config_data.get(
"default",
{"15_min": 4, "hourly": 12, "daily": 0, "monthly": 0},
)
@cache
def load_config_data(config_file: Path) -> dict[str, dict[str, int]]:
"""Load a TOML configuration file.
Args:
config_file (Path): The path to the configuration file.
Returns:
dict: The configuration data.
"""
return tomllib.loads(config_file.read_text())
def get_snapshots_to_delete(
dataset: Dataset,
count_lookup: dict[str, int],
) -> None:
"""Get snapshots to delete.
Args:
dataset (Dataset): the dataset
count_lookup (dict[str, int]): the count lookup
"""
snapshots = dataset.get_snapshots()
if not snapshots:
logger.info(f"{dataset.name} has no snapshots")
return
filters = (
("15_min", re_compile(r"auto_\d{10}(?:15|30|45)")),
("hourly", re_compile(r"auto_\d{8}(?!00)\d{2}00")),
("daily", re_compile(r"auto_\d{6}(?!01)\d{2}0000")),
("monthly", re_compile(r"auto_\d{6}010000")),
)
for filter_name, snapshot_filter in filters:
logger.debug(f"{filter_name=}\n{snapshot_filter=}")
filtered_snapshots = sorted(snapshot.name for snapshot in snapshots if search(snapshot_filter, snapshot.name))
logger.debug(f"{filtered_snapshots=}")
snapshots_wanted = count_lookup[filter_name]
snapshots_being_deleted = filtered_snapshots[:-snapshots_wanted] if snapshots_wanted > 0 else filtered_snapshots
logger.info(f"{snapshots_being_deleted} are being deleted")
for snapshot in snapshots_being_deleted:
if error := dataset.delete_snapshot(snapshot):
error_message = f"{dataset.name}@{snapshot} failed to delete: {error}"
signal_alert(error_message)
logger.error(error_message)
def get_time_stamp() -> str:
"""Get the time stamp."""
now = utcnow()
nearest_15_min = now.replace(minute=(now.minute - (now.minute % 15)))
return nearest_15_min.strftime("auto_%Y%m%d%H%M")
def cli() -> None:
"""CLI."""
typer.run(main)
if __name__ == "__main__":
cli()

11
python/zfs/__init__.py Normal file
View File

@@ -0,0 +1,11 @@
"""init."""
from python.zfs.dataset import Dataset, Snapshot, get_datasets
from python.zfs.zpool import Zpool
__all__ = [
"Dataset",
"Snapshot",
"Zpool",
"get_datasets",
]

214
python/zfs/dataset.py Normal file
View File

@@ -0,0 +1,214 @@
"""dataset."""
from __future__ import annotations
import json
import logging
from datetime import UTC, datetime
from typing import Any
from python.common import bash_wrapper
logger = logging.getLogger(__name__)
def _zfs_list(zfs_list: str) -> dict[str, Any]:
"""Check the version of zfs."""
raw_zfs_list_data, _ = bash_wrapper(zfs_list)
zfs_list_data = json.loads(raw_zfs_list_data)
vers_major = zfs_list_data["output_version"]["vers_major"]
vers_minor = zfs_list_data["output_version"]["vers_minor"]
command = zfs_list_data["output_version"]["command"]
if vers_major != 0 or vers_minor != 1 or command != "zfs list":
error = f"Datasets are not in the correct format {vers_major=} {vers_minor=} {command=}"
raise RuntimeError(error)
return zfs_list_data
class Snapshot:
"""Snapshot."""
def __init__(self, snapshot_data: dict[str, Any]) -> None:
"""__init__."""
properties = snapshot_data["properties"]
self.createtxg = int(snapshot_data["createtxg"])
self.creation = datetime.fromtimestamp(int(properties["creation"]["value"]), tz=UTC)
self.defer_destroy = properties["defer_destroy"]["value"]
self.guid = int(properties["guid"]["value"])
self.name = snapshot_data["name"].split("@")[1]
self.objsetid = int(properties["objsetid"]["value"])
self.referenced = int(properties["referenced"]["value"])
self.used = int(properties["used"]["value"])
self.userrefs = int(properties["userrefs"]["value"])
self.version = int(properties["version"]["value"])
self.written = int(properties["written"]["value"])
def __repr__(self) -> str:
"""__repr__."""
return f"name={self.name} used={self.used} refer={self.referenced}"
class Dataset:
"""Dataset."""
def __init__(self, name: str) -> None:
"""__init__."""
dataset_data = _zfs_list(f"zfs list {name} -pHj -o all")
properties = dataset_data["datasets"][name]["properties"]
self.aclinherit = properties["aclinherit"]["value"]
self.aclmode = properties["aclmode"]["value"]
self.acltype = properties["acltype"]["value"]
self.available = int(properties["available"]["value"])
self.canmount = properties["canmount"]["value"]
self.checksum = properties["checksum"]["value"]
self.clones = properties["clones"]["value"]
self.compression = properties["compression"]["value"]
self.copies = int(properties["copies"]["value"])
self.createtxg = int(properties["createtxg"]["value"])
self.creation = datetime.fromtimestamp(int(properties["creation"]["value"]), tz=UTC)
self.dedup = properties["dedup"]["value"]
self.devices = properties["devices"]["value"]
self.encryption = properties["encryption"]["value"]
self.exec = properties["exec"]["value"]
self.filesystem_limit = properties["filesystem_limit"]["value"]
self.guid = int(properties["guid"]["value"])
self.keystatus = properties["keystatus"]["value"]
self.logbias = properties["logbias"]["value"]
self.mlslabel = properties["mlslabel"]["value"]
self.mounted = properties["mounted"]["value"]
self.mountpoint = properties["mountpoint"]["value"]
self.name = name
self.quota = int(properties["quota"]["value"])
self.readonly = properties["readonly"]["value"]
self.recordsize = int(properties["recordsize"]["value"])
self.redundant_metadata = properties["redundant_metadata"]["value"]
self.referenced = int(properties["referenced"]["value"])
self.refquota = int(properties["refquota"]["value"])
self.refreservation = int(properties["refreservation"]["value"])
self.reservation = int(properties["reservation"]["value"])
self.setuid = properties["setuid"]["value"]
self.sharenfs = properties["sharenfs"]["value"]
self.snapdir = properties["snapdir"]["value"]
self.snapshot_limit = properties["snapshot_limit"]["value"]
self.sync = properties["sync"]["value"]
self.used = int(properties["used"]["value"])
self.usedbychildren = int(properties["usedbychildren"]["value"])
self.usedbydataset = int(properties["usedbydataset"]["value"])
self.usedbysnapshots = int(properties["usedbysnapshots"]["value"])
self.version = int(properties["version"]["value"])
self.volmode = properties["volmode"]["value"]
self.volsize = properties["volsize"]["value"]
self.vscan = properties["vscan"]["value"]
self.written = int(properties["written"]["value"])
self.xattr = properties["xattr"]["value"]
def get_snapshots(self) -> list[Snapshot] | None:
"""Get all snapshots from zfs and process then is test dicts of sets."""
snapshots_data = _zfs_list(f"zfs list -t snapshot -pHj {self.name} -o all")
return [Snapshot(properties) for properties in snapshots_data["datasets"].values()]
def create_snapshot(self, snapshot_name: str) -> str:
"""Creates a zfs snapshot.
Args:
snapshot_name (str): a snapshot name
"""
logger.debug(f"Creating {self.name}@{snapshot_name}")
_, return_code = bash_wrapper(f"zfs snapshot {self.name}@{snapshot_name}")
if return_code == 0:
return "snapshot created"
if snapshots := self.get_snapshots():
snapshot_names = {snapshot.name for snapshot in snapshots}
if snapshot_name in snapshot_names:
return f"Snapshot {snapshot_name} already exists for {self.name}"
return f"Failed to create snapshot {snapshot_name} for {self.name}"
def delete_snapshot(self, snapshot_name: str) -> str | None:
"""Deletes a zfs snapshot.
Args:
snapshot_name (str): a snapshot name
"""
logger.debug(f"deleting {self.name}@{snapshot_name}")
msg, return_code = bash_wrapper(f"zfs destroy {self.name}@{snapshot_name}")
if return_code != 0:
if msg.startswith(f"cannot destroy '{self.name}@{snapshot_name}': snapshot has dependent clones"):
return "snapshot has dependent clones"
error = f"Failed to delete snapshot {snapshot_name=} for {self.name}"
raise RuntimeError(error)
return None
def __repr__(self) -> str:
"""__repr__."""
return (
f"{self.aclinherit=}\n"
f"{self.aclmode=}\n"
f"{self.acltype=}\n"
f"{self.available=}\n"
f"{self.canmount=}\n"
f"{self.checksum=}\n"
f"{self.clones=}\n"
f"{self.compression=}\n"
f"{self.copies=}\n"
f"{self.createtxg=}\n"
f"{self.creation=}\n"
f"{self.dedup=}\n"
f"{self.devices=}\n"
f"{self.encryption=}\n"
f"{self.exec=}\n"
f"{self.filesystem_limit=}\n"
f"{self.guid=}\n"
f"{self.keystatus=}\n"
f"{self.logbias=}\n"
f"{self.mlslabel=}\n"
f"{self.mounted=}\n"
f"{self.mountpoint=}\n"
f"{self.name=}\n"
f"{self.quota=}\n"
f"{self.readonly=}\n"
f"{self.recordsize=}\n"
f"{self.redundant_metadata=}\n"
f"{self.referenced=}\n"
f"{self.refquota=}\n"
f"{self.refreservation=}\n"
f"{self.reservation=}\n"
f"{self.setuid=}\n"
f"{self.sharenfs=}\n"
f"{self.snapdir=}\n"
f"{self.snapshot_limit=}\n"
f"{self.sync=}\n"
f"{self.used=}\n"
f"{self.usedbychildren=}\n"
f"{self.usedbydataset=}\n"
f"{self.usedbysnapshots=}\n"
f"{self.version=}\n"
f"{self.volmode=}\n"
f"{self.volsize=}\n"
f"{self.vscan=}\n"
f"{self.written=}\n"
f"{self.xattr=}\n"
)
def get_datasets() -> list[Dataset]:
"""Get zfs list.
Returns:
list[Dataset]: A list of zfs datasets.
"""
logger.info("Getting zfs list")
dataset_names, _ = bash_wrapper("zfs list -Hp -t filesystem -o name")
cleaned_datasets = dataset_names.strip().split("\n")
return [Dataset(dataset_name) for dataset_name in cleaned_datasets if "/" in dataset_name]

86
python/zfs/zpool.py Normal file
View File

@@ -0,0 +1,86 @@
"""test."""
from __future__ import annotations
import json
from typing import Any
from python.common import bash_wrapper
def _zpool_list(zfs_list: str) -> dict[str, Any]:
"""Check the version of zfs."""
raw_zfs_list_data, _ = bash_wrapper(zfs_list)
zfs_list_data = json.loads(raw_zfs_list_data)
vers_major = zfs_list_data["output_version"]["vers_major"]
vers_minor = zfs_list_data["output_version"]["vers_minor"]
command = zfs_list_data["output_version"]["command"]
if vers_major != 0 or vers_minor != 1 or command != "zpool list":
error = f"Datasets are not in the correct format {vers_major=} {vers_minor=} {command=}"
raise RuntimeError(error)
return zfs_list_data
class Zpool:
"""Zpool."""
def __init__(
self,
name: str,
) -> None:
"""__init__."""
zpool_data = _zpool_list(f"zpool list {name} -pHj -o all")
properties = zpool_data["pools"][name]["properties"]
self.name = name
self.allocated = int(properties["allocated"]["value"])
self.altroot = properties["altroot"]["value"]
self.ashift = int(properties["ashift"]["value"])
self.autoexpand = properties["autoexpand"]["value"]
self.autoreplace = properties["autoreplace"]["value"]
self.autotrim = properties["autotrim"]["value"]
self.capacity = int(properties["capacity"]["value"])
self.comment = properties["comment"]["value"]
self.dedupratio = properties["dedupratio"]["value"]
self.delegation = properties["delegation"]["value"]
self.expandsize = properties["expandsize"]["value"]
self.failmode = properties["failmode"]["value"]
self.fragmentation = int(properties["fragmentation"]["value"])
self.free = properties["free"]["value"]
self.freeing = int(properties["freeing"]["value"])
self.guid = int(properties["guid"]["value"])
self.health = properties["health"]["value"]
self.leaked = int(properties["leaked"]["value"])
self.readonly = properties["readonly"]["value"]
self.size = int(properties["size"]["value"])
def __repr__(self) -> str:
"""__repr__."""
return (
f"{self.name=}\n"
f"{self.allocated=}\n"
f"{self.altroot=}\n"
f"{self.ashift=}\n"
f"{self.autoexpand=}\n"
f"{self.autoreplace=}\n"
f"{self.autotrim=}\n"
f"{self.capacity=}\n"
f"{self.comment=}\n"
f"{self.dedupratio=}\n"
f"{self.delegation=}\n"
f"{self.expandsize=}\n"
f"{self.failmode=}\n"
f"{self.fragmentation=}\n"
f"{self.freeing=}\n"
f"{self.guid=}\n"
f"{self.health=}\n"
f"{self.leaked=}\n"
f"{self.readonly=}\n"
f"{self.size=}"
)

View File

@@ -9,6 +9,7 @@
nix
home-manager
git
my_python
ssh-to-age
gnupg

View File

@@ -1,21 +1,21 @@
{ inputs, ... }:
{
imports = [
../../users/richie
../../users/gaming
../../common/global
../../common/optional/desktop.nix
../../common/optional/docker.nix
../../common/optional/scanner.nix
../../common/optional/steam.nix
../../common/optional/syncthing_base.nix
../../common/optional/systemd-boot.nix
../../common/optional/update.nix
../../common/optional/yubikey.nix
../../common/optional/zerotier.nix
../../common/optional/nvidia.nix
"${inputs.self}/users/richie"
"${inputs.self}/users/gaming"
"${inputs.self}/common/global"
"${inputs.self}/common/optional/desktop.nix"
"${inputs.self}/common/optional/docker.nix"
"${inputs.self}/common/optional/scanner.nix"
"${inputs.self}/common/optional/steam.nix"
"${inputs.self}/common/optional/syncthing_base.nix"
"${inputs.self}/common/optional/systemd-boot.nix"
"${inputs.self}/common/optional/update.nix"
"${inputs.self}/common/optional/yubikey.nix"
"${inputs.self}/common/optional/zerotier.nix"
"${inputs.self}/common/optional/nvidia.nix"
./hardware.nix
./syncthing.nix
./games.nix
./llms.nix
];

View File

@@ -3,6 +3,7 @@
"dotfiles" = {
path = "/home/richie/dotfiles";
devices = [
"brain"
"jeeves"
"rhapsody-in-green"
];
@@ -12,8 +13,9 @@
id = "4ckma-gtshs"; # cspell:disable-line
path = "/home/richie/important";
devices = [
"phone"
"brain"
"jeeves"
"phone"
"rhapsody-in-green"
];
fsWatcherEnabled = true;

39
systems/brain/default.nix Normal file
View File

@@ -0,0 +1,39 @@
{ inputs, ... }:
{
imports = [
"${inputs.self}/users/richie"
"${inputs.self}/common/global"
"${inputs.self}/common/optional/docker.nix"
"${inputs.self}/common/optional/ssh_decrypt.nix"
"${inputs.self}/common/optional/syncthing_base.nix"
"${inputs.self}/common/optional/systemd-boot.nix"
"${inputs.self}/common/optional/update.nix"
"${inputs.self}/common/optional/zerotier.nix"
./docker
./hardware.nix
./programs.nix
./services
./syncthing.nix
inputs.nixos-hardware.nixosModules.framework-11th-gen-intel
];
networking = {
hostName = "brain";
hostId = "93a06c6e";
firewall.enable = true;
networkmanager.enable = true;
};
hardware.bluetooth = {
enable = true;
powerOnBoot = true;
};
services = {
openssh.ports = [ 129 ];
smartd.enable = true;
};
system.stateVersion = "25.05";
}

View File

@@ -0,0 +1,11 @@
{ lib, ... }:
{
imports =
let
files = builtins.attrNames (builtins.readDir ./.);
nixFiles = builtins.filter (name: lib.hasSuffix ".nix" name && name != "default.nix") files;
in
map (file: ./. + "/${file}") nixFiles;
virtualisation.oci-containers.backend = "docker";
}

View File

@@ -0,0 +1,3 @@
# docker_networks
docker network create -d bridge web

View File

@@ -0,0 +1,71 @@
{
config,
lib,
modulesPath,
...
}:
{
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
boot = {
initrd = {
availableKernelModules = [
"ahci"
"ehci_pci"
"nvme"
"sd_mod"
"uas"
"usb_storage"
"usbhid"
"xhci_pci"
];
kernelModules = [ ];
luks.devices."luks-root-pool-nvme-Samsung_SSD_990_PRO_2TB_S7KHNJ0Y121613P-part2" = {
device = "/dev/disk/by-id/nvme-Samsung_SSD_990_PRO_2TB_S7KHNJ0Y121613P-part2";
bypassWorkqueues = true;
allowDiscards = true;
keyFileSize = 4096;
keyFile = "/dev/disk/by-id/usb-USB_SanDisk_3.2Gen1_03021630090925173333-0:0";
fallbackToPassword = true;
};
};
kernelModules = [ "kvm-intel" ];
extraModulePackages = [ ];
};
fileSystems = {
"/" = lib.mkDefault {
device = "root_pool/root";
fsType = "zfs";
};
"/home" = {
device = "root_pool/home";
fsType = "zfs";
};
"/var" = {
device = "root_pool/var";
fsType = "zfs";
};
"/nix" = {
device = "root_pool/nix";
fsType = "zfs";
};
"/boot" = {
device = "/dev/disk/by-uuid/12CE-A600";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@@ -1,7 +1,7 @@
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [
osu-lazer-bin
jellyfin-media-player
filebot
docker-compose
];
}

View File

@@ -0,0 +1,9 @@
{ lib, ... }:
{
imports =
let
files = builtins.attrNames (builtins.readDir ./.);
nixFiles = builtins.filter (name: lib.hasSuffix ".nix" name && name != "default.nix") files;
in
map (file: ./. + "/${file}") nixFiles;
}

View File

@@ -0,0 +1,82 @@
{
users = {
users.hass = {
isSystemUser = true;
group = "hass";
};
groups.hass = { };
};
services = {
home-assistant = {
enable = true;
openFirewall = true;
config = {
http = {
server_port = 8123;
server_host = [
"192.168.90.35"
"192.168.95.35"
"127.0.0.1"
];
};
homeassistant = {
time_zone = "America/New_York";
unit_system = "us_customary";
temperature_unit = "F";
packages = {
victron_modbuss = "!include ${./home_assistant/victron_modbuss.yaml}";
battery_sensors = "!include ${./home_assistant/battery_sensors.yaml}";
};
};
recorder = {
db_url = "postgresql://@/hass";
auto_purge = true;
purge_keep_days = 3650;
db_retry_wait = 15;
};
assist_pipeline = { };
backup = { };
bluetooth = { };
config = { };
dhcp = { };
energy = { };
history = { };
homeassistant_alerts = { };
image_upload = { };
logbook = { };
media_source = { };
mobile_app = { };
ssdp = { };
sun = { };
webhook = { };
cloud = { };
zeroconf = { };
automation = "!include automations.yaml";
script = "!include scripts.yaml";
scene = "!include scenes.yaml";
group = "!include groups.yaml";
};
extraPackages =
python3Packages: with python3Packages; [
aioesphomeapi # for esphome
bleak-esphome # for esphome
esphome-dashboard-api # for esphome
forecast-solar # for solar forecast
gtts # not sure what wants this
jellyfin-apiclient-python # for jellyfin
paho-mqtt # for mqtt
psycopg2 # for postgresql
py-improv-ble-client # for esphome
pymodbus # for modbus
pyopenweathermap # for weather
];
extraComponents = [ "isal" ];
};
esphome = {
enable = true;
openFirewall = true;
address = "192.168.90.35";
};
};
}

View File

@@ -0,0 +1,99 @@
template:
- sensor:
# Battery 0
- name: "JK0 charge power W"
unique_id: jk0_charge_power_w
unit_of_measurement: W
device_class: power
state_class: measurement
state: >
{% set p = states('sensor.batteries_jk0_power')|float(0) %}
{{ max(0, p) }}
- name: "JK0 discharge power W"
unique_id: jk0_discharge_power_w
unit_of_measurement: W
device_class: power
state_class: measurement
state: >
{% set p = states('sensor.batteries_jk0_power')|float(0) %}
{{ max(0, -p) }}
# Battery 1
- name: "JK1 charge power W"
unique_id: jk1_charge_power_w
unit_of_measurement: W
device_class: power
state_class: measurement
state: >
{% set p = states('sensor.batteries_jk1_power')|float(0) %}
{{ max(0, p) }}
- name: "JK1 discharge power W"
unique_id: jk1_discharge_power_w
unit_of_measurement: W
device_class: power
state_class: measurement
state: >
{% set p = states('sensor.batteries_jk1_power')|float(0) %}
{{ max(0, -p) }}
sensor:
# Battery 0
- platform: integration
source: sensor.jk0_charge_power_w
name: "JK0 energy in"
unique_id: jk0_energy_in_kwh
unit_prefix: k
method: trapezoidal
round: 3
max_sub_interval:
minutes: 5
- platform: integration
source: sensor.jk0_discharge_power_w
name: "JK0 energy out"
unique_id: jk0_energy_out_kwh
unit_prefix: k
method: trapezoidal
round: 3
max_sub_interval:
minutes: 5
# Battery 1
- platform: integration
source: sensor.jk1_charge_power_w
name: "JK1 energy in"
unique_id: jk1_energy_in_kwh
unit_prefix: k
method: trapezoidal
round: 3
max_sub_interval:
minutes: 5
- platform: integration
source: sensor.jk1_discharge_power_w
name: "JK1 energy out"
unique_id: jk1_energy_out_kwh
unit_prefix: k
method: trapezoidal
round: 3
max_sub_interval:
minutes: 5
utility_meter:
# Battery 0
jk0_energy_in_daily:
source: sensor.jk0_energy_in
name: "JK0 Energy In Daily"
cycle: daily
jk0_energy_out_daily:
source: sensor.jk0_energy_out
name: "JK0 Energy Out Daily"
cycle: daily
# Battery 1
jk1_energy_in_daily:
source: sensor.jk1_energy_in
name: "JK1 Energy In Daily"
cycle: daily
jk1_energy_out_daily:
source: sensor.jk1_energy_out
name: "JK1 Energy Out Daily"
cycle: daily

View File

@@ -0,0 +1,347 @@
modbus:
- name: victron_gx
type: tcp
host: 192.168.103.30
port: 502
timeout: 3
delay: 2
sensors:
# ---- SOLAR CHARGER (Unit ID 226) ----
- name: Solar Voltage
slave: 226
address: 776
input_type: holding
data_type: uint16
scale: 0.01
precision: 2
unit_of_measurement: "V"
device_class: voltage
state_class: measurement
- name: Solar Amperage
slave: 226
address: 777
input_type: holding
data_type: int16
scale: 0.1
precision: 1
unit_of_measurement: "A"
device_class: current
state_class: measurement
- name: Solar Wattage
slave: 226
address: 789
input_type: holding
data_type: uint16
scale: 0.1
unit_of_measurement: "W"
device_class: power
state_class: measurement
- name: Solar Yield Today
slave: 226
address: 784
input_type: holding
data_type: uint16
scale: 0.1
precision: 3
unit_of_measurement: "kWh"
device_class: energy
state_class: total
# DC system
- name: DC Voltage
slave: 100
address: 840
input_type: holding
data_type: uint16
scale: 0.1
precision: 2
unit_of_measurement: "V"
device_class: voltage
state_class: measurement
unique_id: dc_voltage
- name: DC Wattage
slave: 100
address: 860
input_type: holding
data_type: int16
scale: 1
precision: 0
unit_of_measurement: "W"
device_class: power
state_class: measurement
unique_id: dc_wattage
# GPS
- name: GPS Latitude
slave: 1
address: 2800
input_type: holding
data_type: int32
scale: 0.0000001
precision: 7
state_class: measurement
unique_id: gps_latitude
- name: GPS Longitude
slave: 1
address: 2802
input_type: holding
data_type: int32
scale: 0.0000001
precision: 7
state_class: measurement
unique_id: gps_longitude
- name: GPS Course
slave: 1
address: 2804
input_type: holding
data_type: uint16
scale: 0.01
precision: 2
unit_of_measurement: "°"
state_class: measurement
unique_id: gps_course
- name: GPS Speed
slave: 1
address: 2805
input_type: holding
data_type: uint16
scale: 0.01
precision: 2
unit_of_measurement: "m/s"
state_class: measurement
unique_id: gps_speed
- name: GPS Fix
slave: 1
address: 2806
input_type: holding
data_type: uint16
scale: 1
state_class: measurement
unique_id: gps_fix
- name: GPS Satellites
slave: 1
address: 2807
input_type: holding
data_type: uint16
scale: 1
state_class: measurement
unique_id: gps_satellites
- name: GPS Altitude
slave: 1
address: 2808
input_type: holding
data_type: int32
scale: 0.16
precision: 1
unit_of_measurement: "m"
state_class: measurement
unique_id: gps_altitude
# ---- CHARGER (Unit ID 223) ----
- name: Charger Output 1 Voltage
slave: 223
address: 2307
input_type: holding
data_type: uint16
scale: 0.01
precision: 2
unit_of_measurement: "V"
device_class: voltage
state_class: measurement
unique_id: charger_output_1_voltage
- name: Charger Output 1 Current
slave: 223
address: 2308
input_type: holding
data_type: int16
scale: 0.1
precision: 1
unit_of_measurement: "A"
device_class: current
state_class: measurement
unique_id: charger_output_1_current
- name: Charger Output 1 Temperature
slave: 223
address: 2309
input_type: holding
data_type: int16
scale: 0.1
precision: 1
unit_of_measurement: "°C"
device_class: temperature
state_class: measurement
unique_id: charger_output_1_temperature
- name: Charger AC Current
slave: 223
address: 2314
input_type: holding
data_type: int16
scale: 0.1
precision: 1
unit_of_measurement: "A"
device_class: current
state_class: measurement
unique_id: charger_ac_current
- name: Charger AC Current Limit
slave: 223
address: 2316
input_type: holding
data_type: int16
scale: 0.1
precision: 1
unit_of_measurement: "A"
device_class: current
state_class: measurement
unique_id: charger_ac_current_limit
- name: Charger On Off Raw
slave: 223
address: 2317
input_type: holding
data_type: uint16
scale: 1
state_class: measurement
unique_id: charger_on_off_raw
- name: Charger Charge State Raw
slave: 223
address: 2318
input_type: holding
data_type: uint16
scale: 1
state_class: measurement
unique_id: charger_charge_state_raw
- name: Charger Error Code
slave: 223
address: 2319
input_type: holding
data_type: uint16
scale: 1
state_class: measurement
unique_id: charger_error_code
- name: Charger Relay State
slave: 223
address: 2320
input_type: holding
data_type: uint16
scale: 1
state_class: measurement
unique_id: charger_relay_state
- name: Charger Low Voltage Alarm
slave: 223
address: 2321
input_type: holding
data_type: uint16
scale: 1
state_class: measurement
unique_id: charger_low_voltage_alarm
- name: Charger High Voltage Alarm
slave: 223
address: 2322
input_type: holding
data_type: uint16
scale: 1
state_class: measurement
unique_id: charger_high_voltage_alarm
template:
- sensor:
- name: Charger On Off
state: >-
{% set v = states('sensor.charger_on_off_raw')|int %}
{{ {0:'Off',1:'On',2:'Error',3:'Unavailable'}.get(v, 'Unknown') }}
- name: Charger Charge State
state: >-
{% set v = states('sensor.charger_charge_state_raw')|int %}
{{ {
0:'Off',1:'Low Power',2:'Fault',3:'Bulk',4:'Absorption',5:'Float',
6:'Storage',7:'Equalize/Manual',8:'External Control'
}.get(v,'Unknown') }}
- name: "Charger DC Wattage"
unique_id: charger_dc_wattage
unit_of_measurement: "W"
device_class: power
state_class: measurement
state: >-
{% set v = states('sensor.charger_output_1_voltage')|float(0) %}
{% set a = states('sensor.charger_output_1_current')|float(0) %}
{{ (v * a) | round(1) }}
- binary_sensor:
- name: Charger Low Voltage Alarm Active
state: "{{ states('sensor.charger_low_voltage_alarm')|int == 2 }}"
- name: Charger High Voltage Alarm Active
state: "{{ states('sensor.charger_high_voltage_alarm')|int == 2 }}"
sensor:
- platform: integration
source: sensor.dc_wattage
name: DC System Energy
unit_prefix: k
round: 2
method: trapezoidal
max_sub_interval:
minutes: 5
- platform: integration
source: sensor.solar_wattage
name: Solar Yield
unit_prefix: k
round: 2
method: trapezoidal
max_sub_interval:
minutes: 5
- platform: integration
source: sensor.charger_dc_wattage
name: DC Charger Energy
unit_prefix: k
round: 2
method: trapezoidal
max_sub_interval:
minutes: 5
utility_meter:
dc_load_energy_daily:
source: sensor.dc_system_energy
cycle: daily
dc_load_energy_monthly:
source: sensor.dc_system_energy
cycle: monthly
solar_yield_daily:
source: sensor.solar_yield
cycle: daily
solar_yield_monthly:
source: sensor.solar_yield
cycle: monthly
charger_dc_wattage_daily:
source: sensor.dc_charger_energy
cycle: daily
charger_dc_wattage_monthly:
source: sensor.dc_charger_energy
cycle: monthly

View File

@@ -0,0 +1,6 @@
{
services.jellyfin = {
enable = true;
openFirewall = true;
};
}

View File

@@ -0,0 +1,151 @@
{ pkgs, ... }:
{
networking.firewall.allowedTCPPorts = [ 5432 ];
services.postgresql = {
enable = true;
package = pkgs.postgresql_17_jit;
enableTCPIP = true;
enableJIT = true;
authentication = pkgs.lib.mkOverride 10 ''
# admins
local all postgres trust
host all postgres 127.0.0.1/32 trust
host all postgres ::1/128 trust
local all richie trust
host all richie 127.0.0.1/32 trust
host all richie ::1/128 trust
host all richie 192.168.90.1/24 trust
host all richie 192.168.99.1/24 trust
#type database DBuser origin-address auth-method
local hass hass trust
# ipv4
host hass hass 192.168.90.1/24 trust
host hass hass 127.0.0.1/32 trust
# ipv6
host hass hass ::1/128 trust
'';
identMap = ''
# ArbitraryMapName systemUser DBUser
superuser_map root postgres
superuser_map postgres postgres
# Let other names login as themselves
superuser_map richie postgres
superuser_map hass hass
'';
ensureUsers = [
{
name = "postgres";
ensureClauses = {
superuser = true;
login = true;
createrole = true;
createdb = true;
replication = true;
};
}
{
name = "richie";
ensureDBOwnership = true;
ensureClauses = {
superuser = true;
login = true;
createrole = true;
createdb = true;
replication = true;
};
}
{
name = "hass";
ensureDBOwnership = true;
ensureClauses = {
login = true;
createrole = true;
createdb = true;
replication = true;
};
}
];
ensureDatabases = [
"hass"
"richie"
];
# Thank you NotAShelf
# https://github.com/NotAShelf/nyx/blob/d407b4d6e5ab7f60350af61a3d73a62a5e9ac660/modules/core/roles/server/system/services/databases/postgresql.nix#L74
settings = {
# Connectivity;
max_connections = 100;
superuser_reserved_connections = 3;
# Memory Settings;
shared_buffers = "1024 MB";
work_mem = "32 MB";
maintenance_work_mem = "320 MB";
huge_pages = "off";
effective_cache_size = "2 GB";
effective_io_concurrency = 100; # concurrent IO only really activated if OS supports posix_fadvise function;
random_page_cost = 1.25; # speed of random disk access relative to sequential access (1.0);
# Monitoring;
shared_preload_libraries = "pg_stat_statements,auto_explain"; # per statement resource usage stats & log explain statements for slow queries
track_io_timing = "on"; # measure exact block IO times;
track_functions = "pl"; # track execution times of pl-language procedures if any;
# Replication;
wal_level = "replica"; # consider using at least "replica";
max_wal_senders = 0;
synchronous_commit = "on";
# Checkpointing: ;
checkpoint_timeout = "15 min";
checkpoint_completion_target = 0.9;
max_wal_size = "1024 MB";
min_wal_size = "512 MB";
# WAL writing;
wal_compression = "on";
wal_buffers = -1; # auto-tuned by Postgres till maximum of segment size (16MB by default);
wal_writer_delay = "200ms";
wal_writer_flush_after = "1MB";
# Background writer;
bgwriter_delay = "200ms";
bgwriter_lru_maxpages = 100;
bgwriter_lru_multiplier = 2.0;
bgwriter_flush_after = 0;
# Parallel queries: ;
max_worker_processes = 6;
max_parallel_workers_per_gather = 3;
max_parallel_maintenance_workers = 3;
max_parallel_workers = 6;
parallel_leader_participation = "on";
# Advanced features ;
enable_partitionwise_join = "on";
enable_partitionwise_aggregate = "on";
jit = "on";
jit_above_cost = 100000;
jit_inline_above_cost = 150000;
jit_optimize_above_cost = 500000;
# log slow queries
log_min_duration_statement = 100;
"auto_explain.log_min_duration" = 100;
# logging configuration
log_connections = true;
log_statement = "ddl";
logging_collector = true;
log_disconnections = true;
log_rotation_age = "14d";
};
};
}

View File

@@ -0,0 +1,30 @@
{
networking.firewall.allowedTCPPorts = [ 8384 ];
services.syncthing = {
overrideFolders = false;
guiAddress = "192.168.90.35:8384";
settings = {
"dotfiles" = {
path = "/home/richie/dotfiles";
devices = [
"bob"
"jeeves"
"rhapsody-in-green"
];
fsWatcherEnabled = true;
};
"important" = {
id = "4ckma-gtshs"; # cspell:disable-line
path = "/home/richie/important";
devices = [
"bob"
"jeeves"
"phone"
"rhapsody-in-green"
];
fsWatcherEnabled = true;
};
};
};
}

View File

@@ -1,11 +1,18 @@
{ inputs, ... }:
let
vars = import ./vars.nix;
in
{
imports = [
../../users/richie
../../common/global
../../common/optional/docker.nix
../../common/optional/ssh_decrypt.nix
../../common/optional/syncthing_base.nix
../../common/optional/zerotier.nix
"${inputs.self}/users/richie"
"${inputs.self}/users/math"
"${inputs.self}/users/dov"
"${inputs.self}/common/global"
"${inputs.self}/common/optional/docker.nix"
"${inputs.self}/common/optional/ssh_decrypt.nix"
"${inputs.self}/common/optional/syncthing_base.nix"
"${inputs.self}/common/optional/update.nix"
"${inputs.self}/common/optional/zerotier.nix"
./docker
./services
./hardware.nix
@@ -21,7 +28,12 @@
smartd.enable = true;
snapshot_manager.path = ./snapshot_config.toml;
snapshot_manager = {
path = ./snapshot_config.toml;
EnvironmentFile = "${vars.secrets}/services/snapshot_manager";
};
zerotierone.joinNetworks = [ "a09acf02330d37b9" ];
};
system.stateVersion = "24.05";

View File

@@ -0,0 +1,60 @@
{
config,
pkgs,
...
}:
let
vars = import ../vars.nix;
in
{
# environment.systemPackages = with pkgs; [ php.withExtensions ({ all, ... }: [ all.pdo_pgsql ]) ];
services.httpd = {
enable = true;
adminAddr = "webmaster@localhost";
enablePHP = true;
phpPackage = pkgs.php.withExtensions (
{ enabled, all }:
enabled
++ [
all.pdo
all.pdo_pgsql
]
);
extraModules = [ "rewrite" ];
virtualHosts.great_cloud_of_witnesses = {
hostName = "localhost";
listen = [
{
ip = "*";
port = 8092;
}
];
documentRoot = "${vars.services}/great_cloud_of_witnesses";
extraConfig = ''
<Directory "${vars.services}/great_cloud_of_witnesses">
AllowOverride All
Require all granted
</Directory>
'';
};
};
sops.secrets.gcw_password = {
sopsFile = ../../../users/secrets.yaml;
neededForUsers = true;
};
users = {
users.gcw = {
isSystemUser = true;
hashedPasswordFile = config.sops.secrets.gcw_password.path;
group = "gcw";
};
groups.gcw = { };
};
}

View File

@@ -0,0 +1,21 @@
let
vars = import ../vars.nix;
in
{
networking.firewall.allowedTCPPorts = [
8989
];
virtualisation.oci-containers.containers.signal_cli_rest_api = {
image = "bbernhard/signal-cli-rest-api:latest";
ports = [
"8989:8080"
];
volumes = [
"${vars.docker_configs}/signal-cli-config:/home/.local/share/signal-cli"
];
environment = {
MODE = "json-rpc";
};
autoStart = true;
};
}

View File

@@ -0,0 +1,11 @@
{ inputs, ... }:
let
linuxFirmwareOverlay = final: prev: {
linux-firmware =
inputs.nixpkgs-linux-firmware-20251011.legacyPackages.${final.system}.linux-firmware;
};
in
{
nixpkgs.overlays = [ linuxFirmwareOverlay ];
}

View File

@@ -11,8 +11,8 @@
networks = {
"10-1GB_Primary" = {
matchConfig.Name = "enp98s0f0";
address = [ "192.168.95.14/24" ];
routes = [ { Gateway = "192.168.95.1"; } ];
address = [ "192.168.99.14/24" ];
routes = [ { Gateway = "192.168.99.1"; } ];
linkConfig.RequiredForOnline = "routable";
};
"10-1GB_Secondary" = {

View File

@@ -1,4 +1,9 @@
{ config, lib, ... }:
{
config,
lib,
outputs,
...
}:
with lib;
@@ -64,11 +69,15 @@ in
Host jeeves
Port 629
User github-runners
HostName 192.168.95.14
HostName jeeves
IdentityFile ${vars.secrets}/services/github-runners/id_ed25519_github-runners
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
'';
nixpkgs = {
overlays = builtins.attrValues outputs.overlays;
config.allowUnfree = true;
};
services.github-runners.${name} = {
enable = true;
replace = true;
@@ -83,6 +92,7 @@ in
nixos-rebuild
openssh
treefmt
my_python
];
};
users = {

View File

@@ -27,6 +27,7 @@ sudo zfs create -o recordsize=16k -o primarycache=metadata -o mountpoint=/zfs/me
# scratch datasets
sudo zfs create -o recordsize=16k -o sync=disabled scratch/qbitvpn
sudo zfs create -o recordsize=16k -o sync=disabled scratch/transmission
sudo zfs create -o recordsize=1M scratch/kafka
# storage datasets
sudo zfs create -o recordsize=1M -o compression=zstd-19 storage/archive
@@ -38,3 +39,4 @@ sudo zfs create -o compression=zstd-19 storage/syncthing
sudo zfs create -o recordsize=1M -o compression=zstd-9 -o exec=off -o sync=disabled storage/qbitvpn
sudo zfs create -o recordsize=1M -o compression=zstd-9 -o exec=off -o sync=disabled storage/transmission
sudo zfs create -o recordsize=1M -o compression=zstd-19 storage/library
sudo zfs create -o recordsize=1M -o compression=zstd-19 -o sync=disabled storage/ollama

View File

@@ -1,22 +0,0 @@
{ config, ... }:
let
vars = import ../vars.nix;
in
{
security.acme = {
acceptTerms = true;
defaults = {
email = "themadmaker2@protonmail.com";
dnsResolver = "1.1.1.1:53";
extraLegoFlags = [
"--dns-timeout=300"
];
};
certs."tmmworkshop.com" = {
dnsProvider = "cloudflare";
environmentFile = "${vars.secrets}/services/acme/cloudflare.txt";
email = "themadmaker2@protonmail.com";
group = config.services.haproxy.group;
};
};
}

View File

@@ -22,25 +22,26 @@ defaults
#Application Setup
frontend ContentSwitching
bind *:80 v4v6
bind *:443 v4v6 ssl crt /var/lib/acme/tmmworkshop.com/full.pem
bind *:443 v4v6 ssl crt /zfs/storage/secrets/docker/cloudflare.pem
mode http
# tmmworkshop.com
acl host_tmmworkshop hdr(host) -i tmmworkshop.com
acl host_tmmworkshop hdr(host) -i www.tmmworkshop.com
acl host_audiobookshelf hdr(host) -i audiobookshelf.tmmworkshop.com
acl host_cache hdr(host) -i cache.tmmworkshop.com
acl host_filebrowser hdr(host) -i filebrowser.tmmworkshop.com
acl host_homeassistant hdr(host) -i homeassistant.tmmworkshop.com
acl host_jellyfin hdr(host) -i jellyfin.tmmworkshop.com
acl host_share hdr(host) -i share.tmmworkshop.com
acl host_gcw hdr(host) -i gcw.tmmworkshop.com
acl host_n8n hdr(host) -i n8n.tmmworkshop.com
use_backend tmmworkshop_nodes if host_tmmworkshop
use_backend audiobookshelf_nodes if host_audiobookshelf
use_backend cache_nodes if host_cache
use_backend filebrowser_nodes if host_filebrowser
use_backend homeassistant_nodes if host_homeassistant
use_backend jellyfin if host_jellyfin
use_backend share_nodes if host_share
use_backend gcw_nodes if host_gcw
use_backend n8n if host_n8n
backend audiobookshelf_nodes
mode http
@@ -56,7 +57,7 @@ backend filebrowser_nodes
backend homeassistant_nodes
mode http
server server 127.0.0.1:8123
server server 192.168.90.35:8123
backend jellyfin
option httpchk
@@ -69,6 +70,10 @@ backend share_nodes
mode http
server server 127.0.0.1:8091
backend tmmworkshop_nodes
backend gcw_nodes
mode http
server server 127.0.0.1:8080
server server 127.0.0.1:8092
backend n8n
mode http
server server 127.0.0.1:5678

View File

@@ -19,7 +19,7 @@ in
http = {
server_port = 8123;
server_host = [
"192.168.95.14"
"192.168.99.14"
"192.168.90.40"
"127.0.0.1"
];
@@ -68,11 +68,10 @@ in
jellyfin-apiclient-python
psycopg2
pymetno
pyownet
aio-ownet
rokuecp
uiprotect
wakeonlan
wyoming
];
extraComponents = [ "isal" ];
};
@@ -81,23 +80,5 @@ in
openFirewall = true;
address = "192.168.90.40";
};
wyoming = {
faster-whisper.servers.main = {
enable = true;
uri = "tcp://0.0.0.0:10300";
model = "medium.en";
language = "en";
device = "cuda";
};
piper.servers.main = {
enable = true;
uri = "tcp://0.0.0.0:10200";
voice = "en_GB-alba-medium";
};
openwakeword = {
enable = true;
uri = "tcp://0.0.0.0:10400";
};
};
};
}

View File

@@ -0,0 +1,12 @@
let
vars = import ../vars.nix;
in
{
services.apache-kafka = {
enable = false;
settings = {
listeners = [ "PLAINTEXT://localhost:9092" ];
"log.dirs" = [ vars.kafka ];
};
};
}

View File

@@ -0,0 +1,38 @@
let
vars = import ../vars.nix;
in
{
services = {
ollama = {
user = "ollama";
enable = true;
host = "0.0.0.0";
loadModels = [
"codellama:7b"
"deepseek-r1:14b"
"deepseek-r1:32b"
"deepseek-r1:8b"
"gemma3:12b"
"gemma3:27b"
"gpt-oss:120b"
"gpt-oss:20b"
"qwen3:14b"
"qwen3:30b"
];
models = vars.ollama;
openFirewall = true;
};
};
systemd.services = {
ollama.serviceConfig = {
Nice = 19;
IOSchedulingPriority = 7;
};
ollama-model-loader.serviceConfig = {
Nice = 19;
CPUWeight = 50;
IOSchedulingClass = "idle";
IOSchedulingPriority = 7;
};
};
}

View File

@@ -1,8 +1,10 @@
{ pkgs, ... }:
let
vars = import ../vars.nix;
in
{
services.nix-serve = {
package = pkgs.nix-serve-ng;
enable = true;
secretKeyFile = "${vars.secrets}/services/nix-cache/cache-priv-key.pem";
openFirewall = true;

View File

@@ -23,18 +23,37 @@ in
host all richie 127.0.0.1/32 trust
host all richie ::1/128 trust
host all richie 192.168.90.1/24 trust
host all richie 192.168.95.1/24 trust
host all richie 192.168.99.1/24 trust
#type database DBuser origin-address auth-method
local hass hass trust
local hass hass trust
# ipv4
host hass hass 192.168.90.1/24 trust
host hass hass 127.0.0.1/32 trust
host hass hass 192.168.90.1/24 trust
host hass hass 127.0.0.1/32 trust
# ipv6
host hass hass ::1/128 trust
# megan
host megan megan 192.168.90.1/24 trust
host megan megan 127.0.0.1/32 trust
host gcw megan 192.168.90.1/24 trust
host gcw megan 127.0.0.1/32 trust
# gcw
local gcw gcw trust
host gcw gcw 192.168.90.1/24 trust
host gcw gcw 127.0.0.1/32 trust
# math
local postgres math trust
host postgres math 127.0.0.1/32 trust
host postgres math ::1/128 trust
host postgres math 192.168.90.1/24 trust
'';
identMap = ''
@@ -77,11 +96,45 @@ in
replication = true;
};
}
{
name = "megan";
ensureDBOwnership = true;
ensureClauses = {
login = true;
createrole = true;
createdb = true;
replication = true;
};
}
{
name = "gcw";
ensureDBOwnership = true;
ensureClauses = {
login = true;
createrole = true;
createdb = true;
replication = true;
};
}
{
name = "math";
ensureDBOwnership = true;
ensureClauses = {
login = true;
createrole = true;
createdb = true;
replication = true;
};
}
];
ensureDatabases = [
"gcw"
"hass"
"math"
"megan"
"mxr_dev"
"mxr_prod"
"n8n"
"richie"
];
# Thank you NotAShelf
@@ -149,9 +202,10 @@ in
# logging configuration
log_connections = true;
log_statement = "all";
log_statement = "ddl";
logging_collector = true;
log_disconnections = true;
log_rotation_age = "14d";
};
};
}

View File

@@ -1,6 +1,6 @@
{
inputs,
pkgs,
inputs,
...
}:
let
@@ -22,10 +22,13 @@ in
wantedBy = [ "multi-user.target" ];
description = "validates startup";
path = [ pkgs.zfs ];
environment = {
PYTHONPATH = "${inputs.self}/";
};
serviceConfig = {
EnvironmentFile = "${vars.secrets}/services/server-validation";
Type = "oneshot";
ExecStart = "${inputs.system_tools.packages.x86_64-linux.default}/bin/validate_system --config-file='${./validate_system.toml}'";
ExecStart = "${pkgs.my_python}/bin/python -m python.system_tests.validate_system '${./validate_system.toml}'";
};
};
};

View File

@@ -12,7 +12,7 @@ in
openRPCPort = true;
downloadDirPermissions = "770";
settings = {
bind-address-ipv4 = "192.168.95.14";
bind-address-ipv4 = "192.168.99.14";
cache-size-mb = 0;
download-dir = "${vars.transmission}/complete";
download-queue-enabled = false;

View File

@@ -51,3 +51,45 @@ monthly = 12
hourly = 12
daily = 14
monthly = 2
["media/services"]
15_min = 3
hourly = 12
daily = 14
monthly = 2
["media/home_assistant"]
15_min = 3
hourly = 12
daily = 14
monthly = 2
["scratch/qbitvpn"]
15_min = 0
hourly = 0
daily = 0
monthly = 0
["scratch/transmission"]
15_min = 0
hourly = 0
daily = 0
monthly = 0
["storage/qbitvpn"]
15_min = 0
hourly = 0
daily = 0
monthly = 0
["storage/transmission"]
15_min = 0
hourly = 0
daily = 0
monthly = 0
["storage/ollama"]
15_min = 0
hourly = 0
daily = 0
monthly = 0

View File

@@ -14,6 +14,7 @@ in
path = "/home/richie/dotfiles";
devices = [
"bob"
"brain"
"rhapsody-in-green"
];
fsWatcherEnabled = true;
@@ -23,7 +24,10 @@ in
path = vars.notes;
devices = [
"rhapsody-in-green"
"davids-server"
{
name = "davids-server";
encryptionPasswordFile = "${vars.secrets}/services/syncthing/davids-server";
}
];
fsWatcherEnabled = true;
};
@@ -32,8 +36,9 @@ in
path = "${vars.syncthing}/important";
devices = [
"bob"
"rhapsody-in-green"
"brain"
"phone"
"rhapsody-in-green"
];
fsWatcherEnabled = true;
};
@@ -67,14 +72,20 @@ in
path = "/home/richie/vault";
devices = [
"rhapsody-in-green"
"davids-server"
{
name = "davids-server";
encryptionPasswordFile = "${vars.secrets}/services/syncthing/davids-server";
}
];
fsWatcherEnabled = true;
};
"backup" = {
path = "${vars.syncthing}/backup";
devices = [
"davids-server"
{
name = "davids-server";
encryptionPasswordFile = "${vars.secrets}/services/syncthing/davids-server";
}
];
fsWatcherEnabled = true;
};

View File

@@ -17,5 +17,7 @@ in
share = "${zfs_media}/share";
syncthing = "${zfs_storage}/syncthing";
transmission = "${zfs_storage}/transmission";
ollama = "${zfs_storage}/ollama";
transmission_scratch = "${zfs_scratch}/transmission";
kafka = "${zfs_scratch}/kafka";
}

View File

@@ -0,0 +1,28 @@
{ inputs, ... }:
{
imports = [
"${inputs.self}/users/elise"
"${inputs.self}/users/richie"
"${inputs.self}/common/global"
"${inputs.self}/common/optional/desktop.nix"
"${inputs.self}/common/optional/steam.nix"
"${inputs.self}/common/optional/systemd-boot.nix"
"${inputs.self}/common/optional/update.nix"
"${inputs.self}/common/optional/zerotier.nix"
./hardware.nix
inputs.nixos-hardware.nixosModules.framework-13-7040-amd
];
networking = {
hostName = "leviathan";
hostId = "cb9b64d8";
firewall.enable = true;
networkmanager.enable = true;
};
services = {
openssh.ports = [ 332 ];
};
system.stateVersion = "25.05";
}

View File

@@ -0,0 +1,69 @@
{
config,
lib,
modulesPath,
...
}:
{
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
boot = {
initrd = {
availableKernelModules = [
"ahci"
"ehci_pci"
"nvme"
"sd_mod"
"usb_storage"
"usbhid"
"xhci_pci"
];
kernelModules = [ ];
luks.devices."luks-root-pool-nvme-Samsung_SSD_970_EVO_Plus_1TB_S6S1NS0T617615W-part2" = {
device = "/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_Plus_1TB_S6S1NS0T617615W-part2";
bypassWorkqueues = true;
allowDiscards = true;
};
};
kernelModules = [ "kvm-amd" ];
extraModulePackages = [ ];
};
fileSystems = {
"/" = lib.mkDefault {
device = "root_pool/root";
fsType = "zfs";
};
"/home" = {
device = "root_pool/home";
fsType = "zfs";
};
"/var" = {
device = "root_pool/var";
fsType = "zfs";
};
"/nix" = {
device = "root_pool/nix";
fsType = "zfs";
};
"/boot" = {
device = "/dev/disk/by-uuid/12CE-A600";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
};
swapDevices = [ ];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@@ -1,16 +1,17 @@
{ inputs, ... }:
{
imports = [
../../users/richie
../../common/global
../../common/optional/desktop.nix
../../common/optional/docker.nix
../../common/optional/steam.nix
../../common/optional/syncthing_base.nix
../../common/optional/systemd-boot.nix
../../common/optional/yubikey.nix
../../common/optional/zerotier.nix
"${inputs.self}/users/richie"
"${inputs.self}/common/global"
"${inputs.self}/common/optional/desktop.nix"
"${inputs.self}/common/optional/docker.nix"
"${inputs.self}/common/optional/steam.nix"
"${inputs.self}/common/optional/syncthing_base.nix"
"${inputs.self}/common/optional/systemd-boot.nix"
"${inputs.self}/common/optional/yubikey.nix"
"${inputs.self}/common/optional/zerotier.nix"
./hardware.nix
./llms.nix
./syncthing.nix
inputs.nixos-hardware.nixosModules.framework-13-7040-amd
];
@@ -18,7 +19,10 @@
networking = {
hostName = "rhapsody-in-green";
hostId = "6404140d";
firewall.enable = true;
firewall = {
enable = true;
allowedTCPPorts = [ ];
};
networkmanager.enable = true;
};

View File

@@ -0,0 +1,30 @@
{
services.ollama = {
user = "ollama";
enable = true;
host = "127.0.0.1";
loadModels = [
"codellama:7b"
"deepseek-r1:14b"
"deepseek-r1:32b"
"deepseek-r1:8b"
"gemma3:12b"
"gemma3:27b"
"gpt-oss:20b"
"qwen3:14b"
"qwen3:30b"
];
};
systemd.services = {
ollama.serviceConfig = {
Nice = 19;
IOSchedulingPriority = 7;
};
ollama-model-loader.serviceConfig = {
Nice = 19;
CPUWeight = 50;
IOSchedulingClass = "idle";
IOSchedulingPriority = 7;
};
};
}

View File

@@ -3,8 +3,9 @@
"dotfiles" = {
path = "/home/richie/dotfiles";
devices = [
"jeeves"
"bob"
"brain"
"jeeves"
];
fsWatcherEnabled = true;
};
@@ -21,6 +22,7 @@
path = "/home/richie/important";
devices = [
"bob"
"brain"
"jeeves"
"phone"
];

1
tests/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Tests."""

61
tests/test_common.py Normal file
View File

@@ -0,0 +1,61 @@
"""test_common."""
from __future__ import annotations
from os import environ
from typing import TYPE_CHECKING
from apprise import Apprise
from python.common import bash_wrapper, signal_alert, utcnow
if TYPE_CHECKING:
from pytest_mock import MockerFixture
def test_utcnow() -> None:
"""test_utcnow."""
utcnow()
def test_signal_alert(mocker: MockerFixture) -> None:
"""test_signal_alert."""
environ["SIGNAL_ALERT_FROM_PHONE"] = "1234567890"
environ["SIGNAL_ALERT_TO_PHONE"] = "0987654321"
mock_logger = mocker.patch("python.common.logger")
mock_apprise_client = mocker.MagicMock(spec=Apprise)
mocker.patch("python.common.Apprise", return_value=mock_apprise_client)
signal_alert("test")
mock_logger.info.assert_not_called()
mock_apprise_client.add.assert_called_once_with("signal://localhost:8989/1234567890/0987654321")
mock_apprise_client.notify.assert_called_once_with(title="", body="test")
def test_signal_alert_no_phones(mocker: MockerFixture) -> None:
"""test_signal_alert_no_phones."""
if "SIGNAL_ALERT_FROM_PHONE" in environ:
del environ["SIGNAL_ALERT_FROM_PHONE"]
if "SIGNAL_ALERT_TO_PHONE" in environ:
del environ["SIGNAL_ALERT_TO_PHONE"]
mock_logger = mocker.patch("python.common.logger")
signal_alert("test")
mock_logger.info.assert_called_once_with("SIGNAL_ALERT_FROM_PHONE or SIGNAL_ALERT_TO_PHONE not set")
def test_test_bash_wrapper() -> None:
"""test_test_bash_wrapper."""
stdout, returncode = bash_wrapper("echo test")
assert stdout == "test\n"
assert returncode == 0
def test_test_bash_wrapper_error() -> None:
"""test_test_bash_wrapper_error."""
expected_error = 2
stdout, returncode = bash_wrapper("ls /this/path/does/not/exist")
assert stdout == "ls: cannot access '/this/path/does/not/exist': No such file or directory\n"
assert returncode == expected_error

104
tests/test_components.py Normal file
View File

@@ -0,0 +1,104 @@
"""test_components."""
from pytest_mock import MockerFixture
from python.system_tests.components import systemd_tests, zpool_tests
from python.zfs import Zpool
temp = "Every feature flags pool has all supported and requested features enabled.\n"
SYSTEM_TESTS_COMPONENTS = "python.system_tests.components"
def test_zpool_tests(mocker: MockerFixture) -> None:
"""test_zpool_tests."""
mock_zpool = mocker.MagicMock(spec=Zpool)
mock_zpool.health = "ONLINE"
mock_zpool.capacity = 70
mock_zpool.name = "Main"
mocker.patch(f"{SYSTEM_TESTS_COMPONENTS}.Zpool", return_value=mock_zpool)
mocker.patch(f"{SYSTEM_TESTS_COMPONENTS}.bash_wrapper", return_value=(temp, ""))
errors = zpool_tests(("Main",))
assert errors == []
def test_zpool_tests_out_of_date(mocker: MockerFixture) -> None:
"""test_zpool_tests_out_of_date."""
mock_zpool = mocker.MagicMock(spec=Zpool)
mock_zpool.health = "ONLINE"
mock_zpool.capacity = 70
mock_zpool.name = "Main"
mocker.patch(f"{SYSTEM_TESTS_COMPONENTS}.Zpool", return_value=mock_zpool)
mocker.patch(f"{SYSTEM_TESTS_COMPONENTS}.bash_wrapper", return_value=("", ""))
errors = zpool_tests(("Main",))
assert errors == ["ZPool out of date run `sudo zpool upgrade -a`"]
def test_zpool_tests_out_of_space(mocker: MockerFixture) -> None:
"""test_zpool_tests_out_of_space."""
mock_zpool = mocker.MagicMock(spec=Zpool)
mock_zpool.health = "ONLINE"
mock_zpool.capacity = 100
mock_zpool.name = "Main"
mocker.patch(f"{SYSTEM_TESTS_COMPONENTS}.Zpool", return_value=mock_zpool)
mocker.patch(f"{SYSTEM_TESTS_COMPONENTS}.bash_wrapper", return_value=(temp, ""))
errors = zpool_tests(("Main",))
assert errors == ["Main is low on space"]
def test_zpool_tests_offline(mocker: MockerFixture) -> None:
"""test_zpool_tests_offline."""
mock_zpool = mocker.MagicMock(spec=Zpool)
mock_zpool.health = "OFFLINE"
mock_zpool.capacity = 70
mock_zpool.name = "Main"
mocker.patch(f"{SYSTEM_TESTS_COMPONENTS}.Zpool", return_value=mock_zpool)
mocker.patch(f"{SYSTEM_TESTS_COMPONENTS}.bash_wrapper", return_value=(temp, ""))
errors = zpool_tests(("Main",))
assert errors == ["Main is OFFLINE"]
def test_systemd_tests(mocker: MockerFixture) -> None:
"""test_systemd_tests."""
mocker.patch(
f"{SYSTEM_TESTS_COMPONENTS}.bash_wrapper",
side_effect=[
("inactive\n", ""),
("active\n", ""),
],
)
errors = systemd_tests(("docker",))
assert errors == []
"""test_systemd_tests."""
def test_systemd_tests_multiple_negative_retries(mocker: MockerFixture) -> None:
"""test_systemd_tests_fail."""
mocker.patch(f"{SYSTEM_TESTS_COMPONENTS}.bash_wrapper", return_value=("active\n", ""))
errors = systemd_tests(("docker",), max_retries=-1, retry_delay_secs=-1)
assert errors == []
def test_systemd_tests_multiple_pass(mocker: MockerFixture) -> None:
"""test_systemd_tests_fail."""
mocker.patch(
f"{SYSTEM_TESTS_COMPONENTS}.bash_wrapper",
side_effect=[
("inactive\n", ""),
("activating\n", ""),
("active\n", ""),
],
)
errors = systemd_tests(
("docker",),
retryable_statuses=("inactive\n", "activating\n"),
valid_statuses=("active\n",),
)
assert errors == []
def test_systemd_tests_fail(mocker: MockerFixture) -> None:
"""test_systemd_tests_fail."""
mocker.patch(f"{SYSTEM_TESTS_COMPONENTS}.bash_wrapper", return_value=("inactive\n", ""))
errors = systemd_tests(("docker",), max_retries=5)
assert errors == ["docker is inactive"]

67
tests/test_databasse.py Normal file
View File

@@ -0,0 +1,67 @@
"""test_database."""
from __future__ import annotations
from typing import TYPE_CHECKING
import pytest
from sqlalchemy import Integer, String, create_engine, select
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column, sessionmaker
from python.database import safe_insert
if TYPE_CHECKING:
from collections.abc import Generator
class TestingBase(DeclarativeBase):
"""TestingBase."""
class Item(TestingBase):
"""Item."""
__tablename__ = "items"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(50), nullable=False, unique=True)
@pytest.fixture
def session() -> Generator[Session]:
"""Fresh in-memory DB + tables for each test."""
engine = create_engine("sqlite+pysqlite:///:memory:", echo=False, future=True)
TestingBase.metadata.create_all(engine)
with sessionmaker(bind=engine, expire_on_commit=False, future=True)() as s:
yield s
def test_partial_failure_unique_constraint(session: Session) -> None:
"""Duplicate name should fail only for the conflicting row; others commit."""
objs = [Item(name="a"), Item(name="b"), Item(name="a"), Item(name="c")]
failures = safe_insert(objs, session)
assert len(failures) == 1
exc, failed_obj = failures[0]
assert isinstance(exc, Exception)
assert isinstance(failed_obj, Item)
assert failed_obj.name == "a"
rows = session.scalars(select(Item.name)).all()
assert sorted(rows) == ["a", "b", "c"]
assert rows.count("a") == 1
def test_all_good_inserts(session: Session) -> None:
"""No failures when all rows are valid."""
objs = [Item(name="x"), Item(name="y")]
failures = safe_insert(objs, session)
assert failures == []
rows = session.scalars(select(Item.name).where(Item.name.in_(("x", "y")))).all()
assert sorted(rows) == ["x", "y"]
def test_unmapped_object_raises(session: Session) -> None:
"""Non-ORM instances should raise TypeError immediately."""
with pytest.raises(TypeError):
safe_insert([object()], session)

123
tests/test_parallelize.py Normal file
View File

@@ -0,0 +1,123 @@
"""test_executors."""
from __future__ import annotations
import logging
from concurrent.futures import Future, ThreadPoolExecutor
from typing import TYPE_CHECKING, Any
import pytest
from python.parallelize import _parallelize_base, parallelize_process, parallelize_thread
if TYPE_CHECKING:
from collections.abc import Callable
from pytest_mock import MockerFixture
class MockFuture(Future):
"""MockFuture."""
def __init__(self, result: Any) -> None: # noqa: ANN401
"""Init."""
super().__init__()
self._result = result
self._exception: BaseException | None = None
self.set_result(result)
def exception(self, timeout: float | None = None) -> BaseException | None:
"""Exception."""
logging.debug(f"{timeout}=")
return self._exception
def result(self, timeout: float | None = None) -> Any: # noqa: ANN401
"""Result."""
logging.debug(f"{timeout}=")
return self._result
class MockPoolExecutor(ThreadPoolExecutor):
"""MockPoolExecutor."""
def __init__(self, *args: Any, **kwargs: Any) -> None: # noqa: ANN401
"""Initializes a new ThreadPoolExecutor instance."""
super().__init__(*args, **kwargs)
def submit(self, fn: Callable[..., Any], /, *args: Any, **kwargs: Any) -> Future: # noqa: ANN401
"""Submits a callable to be executed with the given arguments.
Args:
fn: The callable to execute.
*args: The positional arguments to pass to the callable.
**kwargs: The keyword arguments to pass to the callable.
Returns:
A Future instance representing the execution of the callable.
"""
result = fn(*args, **kwargs)
return MockFuture(result)
def add(a: int, b: int) -> int:
"""Add."""
return a + b
def test_parallelize_thread() -> None:
"""test_parallelize_thread."""
kwargs_list = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
results = parallelize_thread(func=add, kwargs_list=kwargs_list, progress_tracker=1)
assert results.results == [3, 7]
assert not results.exceptions
def test_parallelize_thread_exception() -> None:
"""test_parallelize_thread."""
kwargs_list: list[dict[str, int | None]] = [{"a": 1, "b": 2}, {"a": 3, "b": None}]
results = parallelize_thread(func=add, kwargs_list=kwargs_list)
assert results.results == [3]
output = """[TypeError("unsupported operand type(s) for +: 'int' and 'NoneType'")]"""
assert str(results.exceptions) == output
def test_parallelize_process() -> None:
"""test_parallelize_process."""
kwargs_list = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
results = parallelize_process(func=add, kwargs_list=kwargs_list)
assert results.results == [3, 7]
assert not results.exceptions
def test_parallelize_process_to_many_max_workers(mocker: MockerFixture) -> None:
"""test_parallelize_process."""
mocker.patch(target="python.parallelize.cpu_count", return_value=1)
with pytest.raises(RuntimeError, match="max_workers must be less than or equal to 1"):
parallelize_process(func=add, kwargs_list=[{"a": 1, "b": 2}], max_workers=8)
def test_executor_results_repr() -> None:
"""test_ExecutorResults_repr."""
results = parallelize_thread(func=add, kwargs_list=[{"a": 1, "b": 2}])
assert repr(results) == "results=[3] exceptions=[]"
def test_early_error() -> None:
"""test_early_error."""
kwargs_list: list[dict[str, int | None]] = [{"a": 1, "b": 2}, {"a": 3, "b": None}]
with pytest.raises(TypeError, match=r"unsupported operand type\(s\) for \+\: 'int' and 'NoneType'"):
parallelize_thread(func=add, kwargs_list=kwargs_list, mode="early_error")
def test_mock_pool_executor() -> None:
"""test_mock_pool_executor."""
results = _parallelize_base(
executor_type=MockPoolExecutor,
func=add,
kwargs_list=[{"a": 1, "b": 2}, {"a": 3, "b": 4}],
max_workers=None,
progress_tracker=None,
mode="normal",
)
assert repr(results) == "results=[3, 7] exceptions=[]"

View File

@@ -0,0 +1,60 @@
"""test_server_validate_scripts."""
from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING
import pytest
from pytest_mock import MockerFixture
from python.system_tests.validate_system import main
if TYPE_CHECKING:
from pyfakefs.fake_filesystem import FakeFilesystem
from pytest_mock import MockerFixture
VALIDATE_SYSTEM = "python.system_tests.validate_system"
def test_validate_system(mocker: MockerFixture, fs: FakeFilesystem) -> None:
"""test_validate_system."""
fs.create_file(
"/mock_snapshot_config.toml",
contents='zpool = ["root_pool", "storage", "media"]\nservices = ["docker"]\n',
)
mocker.patch(f"{VALIDATE_SYSTEM}.systemd_tests", return_value=None)
mocker.patch(f"{VALIDATE_SYSTEM}.zpool_tests", return_value=None)
main(Path("/mock_snapshot_config.toml"))
def test_validate_system_errors(mocker: MockerFixture, fs: FakeFilesystem) -> None:
"""test_validate_system_errors."""
fs.create_file(
"/mock_snapshot_config.toml",
contents='zpool = ["root_pool", "storage", "media"]\nservices = ["docker"]\n',
)
mocker.patch(f"{VALIDATE_SYSTEM}.systemd_tests", return_value=["systemd_tests error"])
mocker.patch(f"{VALIDATE_SYSTEM}.zpool_tests", return_value=["zpool_tests error"])
with pytest.raises(SystemExit) as exception_info:
main(Path("/mock_snapshot_config.toml"))
assert exception_info.value.code == 1
def test_validate_system_execution(mocker: MockerFixture, fs: FakeFilesystem) -> None:
"""test_validate_system_execution."""
fs.create_file(
"/mock_snapshot_config.toml",
contents='zpool = ["root_pool", "storage", "media"]\nservices = ["docker"]\n',
)
mocker.patch(f"{VALIDATE_SYSTEM}.zpool_tests", side_effect=RuntimeError("zpool_tests error"))
with pytest.raises(SystemExit) as exception_info:
main(Path("/mock_snapshot_config.toml"))
assert exception_info.value.code == 1

View File

@@ -0,0 +1,167 @@
"""test_snapshot_manager."""
from __future__ import annotations
from datetime import UTC, datetime
from pathlib import Path
from typing import TYPE_CHECKING
import pytest
from python.tools.snapshot_manager import get_snapshots_to_delete, get_time_stamp, load_config_data, main
from python.zfs.dataset import Dataset, Snapshot
if TYPE_CHECKING:
from pyfakefs.fake_filesystem import FakeFilesystem
from pytest_mock import MockerFixture
SNAPSHOT_MANAGER = "python.tools.snapshot_manager"
def patch_utcnow(mocker: MockerFixture, datetime_value: datetime) -> None:
"""patch_utcnow."""
mocker.patch("python.tools.snapshot_manager.utcnow", return_value=datetime_value)
def create_mock_snapshot(mocker: MockerFixture, name: str) -> Snapshot:
"""create_mock_snapshot."""
mock_snapshot = mocker.MagicMock(spec=Snapshot)
mock_snapshot.name = name
return mock_snapshot
def test_main(mocker: MockerFixture, fs: FakeFilesystem) -> None:
"""Test main."""
load_config_data.cache_clear()
mocker.patch(f"{SNAPSHOT_MANAGER}.get_time_stamp", return_value="2023-01-01T00:00:00")
mock_dataset = mocker.MagicMock(spec=Dataset)
mock_dataset.name = "test_dataset"
mock_dataset.create_snapshot.return_value = "snapshot created"
mock_get_datasets = mocker.patch(f"{SNAPSHOT_MANAGER}.get_datasets", return_value=(mock_dataset,))
mock_get_snapshots_to_delete = mocker.patch(f"{SNAPSHOT_MANAGER}.get_snapshots_to_delete")
mock_signal_alert = mocker.patch(f"{SNAPSHOT_MANAGER}.signal_alert")
mock_snapshot_config_toml = '["default"]\n15_min = 8\nhourly = 24\ndaily = 0\nmonthly = 0\n'
fs.create_file("/mock_snapshot_config.toml", contents=mock_snapshot_config_toml)
main(Path("/mock_snapshot_config.toml"))
mock_signal_alert.assert_not_called()
mock_get_datasets.assert_called_once()
mock_get_snapshots_to_delete.assert_called_once_with(
mock_dataset,
{
"15_min": 8,
"hourly": 24,
"daily": 0,
"monthly": 0,
},
)
def test_main_create_snapshot_failure(mocker: MockerFixture, fs: FakeFilesystem) -> None:
"""Test main."""
load_config_data.cache_clear()
mocker.patch(f"{SNAPSHOT_MANAGER}.get_time_stamp", return_value="2023-01-01T00:00:00")
mock_dataset = mocker.MagicMock(spec=Dataset)
mock_dataset.name = "test_dataset"
mock_dataset.create_snapshot.return_value = "snapshot not created"
mock_get_datasets = mocker.patch(f"{SNAPSHOT_MANAGER}.get_datasets", return_value=(mock_dataset,))
mock_get_snapshots_to_delete = mocker.patch(f"{SNAPSHOT_MANAGER}.get_snapshots_to_delete")
mock_signal_alert = mocker.patch(f"{SNAPSHOT_MANAGER}.signal_alert")
mock_snapshot_config_toml = '["default"]\n15_min = 8\nhourly = 24\ndaily = 0\nmonthly = 0\n'
fs.create_file("/mock_snapshot_config.toml", contents=mock_snapshot_config_toml)
main(Path("/mock_snapshot_config.toml"))
mock_signal_alert.assert_called_once_with("test_dataset failed to create snapshot 2023-01-01T00:00:00")
mock_get_datasets.assert_called_once()
mock_get_snapshots_to_delete.assert_not_called()
def test_main_exception(mocker: MockerFixture, fs: FakeFilesystem) -> None:
"""Test main."""
load_config_data.cache_clear()
mocker.patch(f"{SNAPSHOT_MANAGER}.get_time_stamp", return_value="2023-01-01T00:00:00")
mock_dataset = mocker.MagicMock(spec=Dataset)
mock_dataset.name = "test_dataset"
mock_dataset.create_snapshot.return_value = "snapshot created"
mock_get_datasets = mocker.patch(f"{SNAPSHOT_MANAGER}.get_datasets", side_effect=Exception("test"))
mock_get_snapshots_to_delete = mocker.patch(f"{SNAPSHOT_MANAGER}.get_snapshots_to_delete")
mock_signal_alert = mocker.patch(f"{SNAPSHOT_MANAGER}.signal_alert")
mock_snapshot_config_toml = '["default"]\n15_min = 8\nhourly = 24\ndaily = 0\nmonthly = 0\n'
fs.create_file("/mock_snapshot_config.toml", contents=mock_snapshot_config_toml)
with pytest.raises(SystemExit) as pytest_wrapped_e:
main(Path("/mock_snapshot_config.toml"))
assert isinstance(pytest_wrapped_e.value, SystemExit)
assert pytest_wrapped_e.value.code == 1
mock_signal_alert.assert_called_once_with("snapshot_manager failed")
mock_get_datasets.assert_called_once()
mock_get_snapshots_to_delete.assert_not_called()
def test_get_snapshots_to_delete(mocker: MockerFixture) -> None:
"""test_get_snapshots_to_delete."""
mock_snapshot_0 = create_mock_snapshot(mocker, "auto_202509150415")
mock_snapshot_1 = create_mock_snapshot(mocker, "auto_202509150415")
mock_dataset = mocker.MagicMock(spec=Dataset)
mock_dataset.name = "test_dataset"
mock_dataset.get_snapshots.return_value = (mock_snapshot_0, mock_snapshot_1)
mock_dataset.delete_snapshot.return_value = None
mock_signal_alert = mocker.patch(f"{SNAPSHOT_MANAGER}.signal_alert")
get_snapshots_to_delete(mock_dataset, {"15_min": 1, "hourly": 0, "daily": 0, "monthly": 0})
mock_signal_alert.assert_not_called()
mock_dataset.delete_snapshot.assert_called_once_with("auto_202509150415")
def test_get_snapshots_to_delete_no_snapshot(mocker: MockerFixture) -> None:
"""test_get_snapshots_to_delete_no_snapshot."""
mock_dataset = mocker.MagicMock(spec=Dataset)
mock_dataset.name = "test_dataset"
mock_dataset.get_snapshots.return_value = ()
mock_dataset.delete_snapshot.return_value = None
mock_signal_alert = mocker.patch(f"{SNAPSHOT_MANAGER}.signal_alert")
get_snapshots_to_delete(mock_dataset, {"15_min": 1, "hourly": 0, "daily": 0, "monthly": 0})
mock_signal_alert.assert_not_called()
mock_dataset.delete_snapshot.assert_not_called()
def test_get_snapshots_to_delete_errored(mocker: MockerFixture) -> None:
"""test_get_snapshots_to_delete_errored."""
mock_snapshot_0 = create_mock_snapshot(mocker, "auto_202509150415")
mock_snapshot_1 = create_mock_snapshot(mocker, "auto_202509150415")
mock_dataset = mocker.MagicMock(spec=Dataset)
mock_dataset.name = "test_dataset"
mock_dataset.get_snapshots.return_value = (mock_snapshot_0, mock_snapshot_1)
mock_dataset.delete_snapshot.return_value = "snapshot has dependent clones"
mock_signal_alert = mocker.patch(f"{SNAPSHOT_MANAGER}.signal_alert")
get_snapshots_to_delete(mock_dataset, {"15_min": 1, "hourly": 0, "daily": 0, "monthly": 0})
mock_signal_alert.assert_called_once_with(
"test_dataset@auto_202509150415 failed to delete: snapshot has dependent clones"
)
mock_dataset.delete_snapshot.assert_called_once_with("auto_202509150415")
def test_get_time_stamp(mocker: MockerFixture) -> None:
"""Test get_time_stamp."""
patch_utcnow(mocker, datetime(2023, 1, 1, 0, 0, 0, tzinfo=UTC))
assert get_time_stamp() == "auto_202301010000"

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