mirror of
https://github.com/RichieCahill/dotfiles.git
synced 2026-04-21 14:49:10 -04:00
Compare commits
59 Commits
feature/bu
...
feature/te
| Author | SHA1 | Date | |
|---|---|---|---|
| 24b66745d1 | |||
| 3074e3c47e | |||
| 9aea31e841 | |||
| ec5975d663 | |||
| f5b22322d0 | |||
| 41d3a8fe1a | |||
|
|
e6ac8f8021 | ||
| 0f8f6f96d6 | |||
| 4cb4bd6f3d | |||
| c046710258 | |||
| 7f9fbe3602 | |||
|
|
8ee3b4d6e5 | ||
| 18b7fb2d60 | |||
| 2f1fa5c750 | |||
| 164d0dd59e | |||
| d4459643ab | |||
| c09dba0c37 | |||
| 409f376166 | |||
| a9a6e1f932 | |||
| 6472f07a88 | |||
|
|
51c79f6b40 | ||
| b0d5147296 | |||
| c56082b516 | |||
| 34b728c88f | |||
| 5697458bad | |||
| 276c2ac74b | |||
| 69e5aa20d5 | |||
| 3d1f773fa5 | |||
| 14dd1fe52e | |||
| 30fe41ea1b | |||
| 3a17c5514d | |||
| c6586db91e | |||
| 81b199373e | |||
| a957e23041 | |||
| 52389f729d | |||
| cc2a609f52 | |||
| ca4693a1ba | |||
| 90e5e0855d | |||
| e339667c2b | |||
| 85540ee920 | |||
| 3be1b8aa8f | |||
| 7c56954cda | |||
| 290f972346 | |||
| 72c3ccfb6d | |||
| 9630633ff5 | |||
| 8c83f306b2 | |||
| 5b4609dc3b | |||
| d1be25c6e8 | |||
| 31910586d2 | |||
| b8dfd0852a | |||
| 6ce622e93e | |||
| 55e652a51d | |||
| b5455a5483 | |||
| 8baf388061 | |||
| 7ffb7b4a37 | |||
| eb04f4a56d | |||
| 5b8e543226 | |||
| da48f62195 | |||
| 60f2ab1039 |
2
.github/workflows/build_systems.yml
vendored
2
.github/workflows/build_systems.yml
vendored
@@ -25,4 +25,4 @@ jobs:
|
||||
- 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
|
||||
run: nix copy --accept-flake-config --to unix:///host-nix/var/nix/daemon-socket/socket .#nixosConfigurations.${{ matrix.system }}.config.system.build.toplevel
|
||||
|
||||
19
.github/workflows/pytest_safe.yml
vendored
Normal file
19
.github/workflows/pytest_safe.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: pytest_safe
|
||||
|
||||
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: unshare --map-root-user --user --net -- pytest tests
|
||||
13
.vscode/settings.json
vendored
13
.vscode/settings.json
vendored
@@ -81,7 +81,6 @@
|
||||
"FASTFOX",
|
||||
"ffmpegthumbnailer",
|
||||
"filebot",
|
||||
"filebrowser",
|
||||
"fileroller",
|
||||
"findbar",
|
||||
"Fira",
|
||||
@@ -98,6 +97,7 @@
|
||||
"getch",
|
||||
"getmaxyx",
|
||||
"ghdeploy",
|
||||
"gitea",
|
||||
"globalprivacycontrol",
|
||||
"gparted",
|
||||
"gtts",
|
||||
@@ -116,7 +116,9 @@
|
||||
"httpchk",
|
||||
"hurlenko",
|
||||
"hwloc",
|
||||
"ical",
|
||||
"ignorelist",
|
||||
"improv",
|
||||
"INITDB",
|
||||
"iocharset",
|
||||
"ioit",
|
||||
@@ -126,6 +128,8 @@
|
||||
"jnoortheen",
|
||||
"jsbc",
|
||||
"kagi",
|
||||
"keyformat",
|
||||
"keylocation",
|
||||
"kuma",
|
||||
"lazer",
|
||||
"levelname",
|
||||
@@ -225,12 +229,9 @@
|
||||
"pylint",
|
||||
"pymetno",
|
||||
"pymodbus",
|
||||
"pyopenweathermap",
|
||||
"pyownet",
|
||||
"pytest",
|
||||
"qbit",
|
||||
"qbittorrent",
|
||||
"qbittorrentvpn",
|
||||
"qbitvpn",
|
||||
"quicksuggest",
|
||||
"radarr",
|
||||
"readahead",
|
||||
@@ -288,9 +289,11 @@
|
||||
"twimg",
|
||||
"typer",
|
||||
"uaccess",
|
||||
"ubiquiti",
|
||||
"ublock",
|
||||
"uiprotect",
|
||||
"uitour",
|
||||
"unifi",
|
||||
"unrar",
|
||||
"unsubmitted",
|
||||
"uptimekuma",
|
||||
|
||||
5
AGENTS.md
Normal file
5
AGENTS.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## Dev environment tips
|
||||
|
||||
- use treefmt to format all files
|
||||
- make python code ruff compliant
|
||||
- use pytest to test python code
|
||||
@@ -16,7 +16,6 @@
|
||||
./nh.nix
|
||||
./nix.nix
|
||||
./programs.nix
|
||||
./safe_reboot.nix
|
||||
./ssh.nix
|
||||
./snapshot_manager.nix
|
||||
];
|
||||
@@ -24,7 +23,7 @@
|
||||
boot = {
|
||||
tmp.useTmpfs = true;
|
||||
kernelPackages = lib.mkDefault pkgs.linuxPackages_6_12;
|
||||
zfs.package = lib.mkDefault pkgs.zfs_2_3;
|
||||
zfs.package = lib.mkDefault pkgs.zfs_2_4;
|
||||
};
|
||||
|
||||
hardware.enableRedistributableFirmware = true;
|
||||
@@ -50,11 +49,6 @@
|
||||
PYTHONPATH = "${inputs.self}/";
|
||||
};
|
||||
|
||||
safe_reboot = {
|
||||
enable = lib.mkDefault true;
|
||||
datasetPrefix = "root_pool/";
|
||||
};
|
||||
|
||||
zfs = {
|
||||
trim.enable = lib.mkDefault true;
|
||||
autoScrub.enable = lib.mkDefault true;
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
{
|
||||
config,
|
||||
inputs,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.safe_reboot;
|
||||
python_command =
|
||||
lib.escapeShellArgs (
|
||||
[
|
||||
"${pkgs.my_python}/bin/python"
|
||||
"-m"
|
||||
"python.tools.safe_reboot"
|
||||
]
|
||||
++ lib.optionals (cfg.drivePath != null) [ cfg.drivePath ]
|
||||
++ [
|
||||
"--dataset-prefix"
|
||||
cfg.datasetPrefix
|
||||
"--check-only"
|
||||
]
|
||||
);
|
||||
in
|
||||
{
|
||||
options.services.safe_reboot = {
|
||||
enable = lib.mkEnableOption "Safe reboot dataset/drive validation";
|
||||
datasetPrefix = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "root_pool/";
|
||||
description = "Dataset prefix that must have exec enabled before rebooting.";
|
||||
};
|
||||
drivePath = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = "Drive path that must exist before rebooting. Set to null to skip.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.services.safe-reboot-check = {
|
||||
description = "Safe reboot validation";
|
||||
before = [ "systemd-reboot.service" ];
|
||||
wantedBy = [ "reboot.target" ];
|
||||
partOf = [ "reboot.target" ];
|
||||
path = [ pkgs.zfs ];
|
||||
environment = {
|
||||
PYTHONPATH = "${inputs.self}/";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = python_command;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
boot = {
|
||||
kernelPackages = pkgs.linuxPackages_6_17;
|
||||
zfs.package = pkgs.zfs_unstable;
|
||||
kernelPackages = pkgs.linuxPackages_6_18;
|
||||
zfs.package = pkgs.zfs_2_4;
|
||||
};
|
||||
|
||||
hardware.bluetooth = {
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
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"
|
||||
132
esphome/battery0.yml
Normal file
132
esphome/battery0.yml
Normal file
@@ -0,0 +1,132 @@
|
||||
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
|
||||
fast_connect: on
|
||||
|
||||
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
|
||||
|
||||
jk_bms_ble:
|
||||
- ble_client_id: jk_ble0
|
||||
protocol_version: JK02_32S
|
||||
throttle: 1s
|
||||
id: jk_bms0
|
||||
|
||||
button:
|
||||
- platform: jk_bms_ble
|
||||
retrieve_settings:
|
||||
name: "JK0 retrieve settings"
|
||||
retrieve_device_info:
|
||||
name: "JK0 retrieve device info"
|
||||
|
||||
sensor:
|
||||
- platform: jk_bms_ble
|
||||
jk_bms_ble_id: jk_bms0
|
||||
total_voltage:
|
||||
name: "JK0 Total Voltage"
|
||||
state_of_charge:
|
||||
name: "JK0 SoC"
|
||||
charging_power:
|
||||
name: "JK0 charging power"
|
||||
discharging_power:
|
||||
name: "JK0 discharging power"
|
||||
temperature_sensor_1:
|
||||
name: "JK0 Temp 1"
|
||||
temperature_sensor_2:
|
||||
name: "JK0 Temp 2"
|
||||
balancing:
|
||||
name: "JK0 balancing"
|
||||
total_runtime:
|
||||
name: "JK0 total runtime"
|
||||
balancing_current:
|
||||
name: "JK0 balancing current"
|
||||
delta_cell_voltage:
|
||||
name: "JK0 cell delta voltage"
|
||||
average_cell_voltage:
|
||||
name: "JK0 cell average voltage"
|
||||
cell_voltage_1:
|
||||
name: "JK0 cell voltage 1"
|
||||
cell_voltage_2:
|
||||
name: "JK0 cell voltage 2"
|
||||
cell_voltage_3:
|
||||
name: "JK0 cell voltage 3"
|
||||
cell_voltage_4:
|
||||
name: "JK0 cell voltage 4"
|
||||
cell_voltage_5:
|
||||
name: "JK0 cell voltage 5"
|
||||
cell_voltage_6:
|
||||
name: "JK0 cell voltage 6"
|
||||
cell_voltage_7:
|
||||
name: "JK0 cell voltage 7"
|
||||
cell_voltage_8:
|
||||
name: "JK0 cell voltage 8"
|
||||
cell_resistance_1:
|
||||
name: "JK0 cell resistance 1"
|
||||
cell_resistance_2:
|
||||
name: "JK0 cell resistance 2"
|
||||
cell_resistance_3:
|
||||
name: "JK0 cell resistance 3"
|
||||
cell_resistance_4:
|
||||
name: "JK0 cell resistance 4"
|
||||
cell_resistance_5:
|
||||
name: "JK0 cell resistance 5"
|
||||
cell_resistance_6:
|
||||
name: "JK0 cell resistance 6"
|
||||
cell_resistance_7:
|
||||
name: "JK0 cell resistance 7"
|
||||
cell_resistance_8:
|
||||
name: "JK0 cell resistance 8"
|
||||
total_charging_cycle_capacity:
|
||||
name: "JK0 total charging cycle capacity"
|
||||
|
||||
text_sensor:
|
||||
- platform: jk_bms_ble
|
||||
jk_bms_ble_id: jk_bms0
|
||||
errors:
|
||||
name: "JK0 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: ble_client
|
||||
ble_client_id: jk_ble0
|
||||
name: "JK0 enable bluetooth connection"
|
||||
id: ble_client_switch0
|
||||
132
esphome/battery1.yml
Normal file
132
esphome/battery1.yml
Normal file
@@ -0,0 +1,132 @@
|
||||
esphome:
|
||||
name: battery1
|
||||
friendly_name: battery1
|
||||
|
||||
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
|
||||
fast_connect: on
|
||||
|
||||
captive_portal:
|
||||
|
||||
esp32_ble_tracker:
|
||||
scan_parameters:
|
||||
interval: 1100ms
|
||||
window: 1100ms
|
||||
active: true
|
||||
|
||||
ble_client:
|
||||
- mac_address: "C8:47:80:37:9D:DD"
|
||||
id: jk_ble1
|
||||
|
||||
jk_bms_ble:
|
||||
- ble_client_id: jk_ble1
|
||||
protocol_version: JK02_32S
|
||||
throttle: 1s
|
||||
id: jk_bms1
|
||||
|
||||
button:
|
||||
- platform: jk_bms_ble
|
||||
retrieve_settings:
|
||||
name: "JK1 retrieve settings"
|
||||
retrieve_device_info:
|
||||
name: "JK1 retrieve device info"
|
||||
|
||||
sensor:
|
||||
- platform: jk_bms_ble
|
||||
jk_bms_ble_id: jk_bms1
|
||||
total_voltage:
|
||||
name: "JK1 Total Voltage"
|
||||
state_of_charge:
|
||||
name: "JK1 SoC"
|
||||
charging_power:
|
||||
name: "JK1 charging power"
|
||||
discharging_power:
|
||||
name: "JK1 discharging power"
|
||||
temperature_sensor_1:
|
||||
name: "JK1 Temp 1"
|
||||
temperature_sensor_2:
|
||||
name: "JK1 Temp 2"
|
||||
balancing:
|
||||
name: "JK1 balancing"
|
||||
total_runtime:
|
||||
name: "JK1 total runtime"
|
||||
balancing_current:
|
||||
name: "JK1 balancing current"
|
||||
delta_cell_voltage:
|
||||
name: "JK1 cell delta voltage"
|
||||
average_cell_voltage:
|
||||
name: "JK1 cell average voltage"
|
||||
cell_voltage_1:
|
||||
name: "JK1 cell voltage 1"
|
||||
cell_voltage_2:
|
||||
name: "JK1 cell voltage 2"
|
||||
cell_voltage_3:
|
||||
name: "JK1 cell voltage 3"
|
||||
cell_voltage_4:
|
||||
name: "JK1 cell voltage 4"
|
||||
cell_voltage_5:
|
||||
name: "JK1 cell voltage 5"
|
||||
cell_voltage_6:
|
||||
name: "JK1 cell voltage 6"
|
||||
cell_voltage_7:
|
||||
name: "JK1 cell voltage 7"
|
||||
cell_voltage_8:
|
||||
name: "JK1 cell voltage 8"
|
||||
cell_resistance_1:
|
||||
name: "JK1 cell resistance 1"
|
||||
cell_resistance_2:
|
||||
name: "JK1 cell resistance 2"
|
||||
cell_resistance_3:
|
||||
name: "JK1 cell resistance 3"
|
||||
cell_resistance_4:
|
||||
name: "JK1 cell resistance 4"
|
||||
cell_resistance_5:
|
||||
name: "JK1 cell resistance 5"
|
||||
cell_resistance_6:
|
||||
name: "JK1 cell resistance 6"
|
||||
cell_resistance_7:
|
||||
name: "JK1 cell resistance 7"
|
||||
cell_resistance_8:
|
||||
name: "JK1 cell resistance 8"
|
||||
total_charging_cycle_capacity:
|
||||
name: "JK1 total charging cycle capacity"
|
||||
|
||||
text_sensor:
|
||||
- platform: jk_bms_ble
|
||||
jk_bms_ble_id: jk_bms1
|
||||
errors:
|
||||
name: "JK1 Errors"
|
||||
|
||||
switch:
|
||||
- platform: jk_bms_ble
|
||||
jk_bms_ble_id: jk_bms1
|
||||
charging:
|
||||
name: "JK1 Charging"
|
||||
discharging:
|
||||
name: "JK1 Discharging"
|
||||
balancer:
|
||||
name: "JK1 Balancing"
|
||||
|
||||
- platform: ble_client
|
||||
ble_client_id: jk_ble1
|
||||
name: "JK1 enable bluetooth connection"
|
||||
id: ble_client_switch0
|
||||
48
esphome/environment.yml
Normal file
48
esphome/environment.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
esphome:
|
||||
name: "environment"
|
||||
friendly_name: "environment"
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: arduino
|
||||
|
||||
i2c:
|
||||
sda: GPIO21
|
||||
scl: GPIO22
|
||||
scan: True
|
||||
id: bus_a
|
||||
|
||||
sensor:
|
||||
- platform: aht10
|
||||
i2c_id: bus_a
|
||||
address: 0x38
|
||||
variant: AHT20
|
||||
temperature:
|
||||
name: "environment Temperature"
|
||||
id: aht10_temperature
|
||||
humidity:
|
||||
name: "environment Humidity"
|
||||
id: aht10_humidity
|
||||
update_interval: 5s
|
||||
|
||||
web_server:
|
||||
port: 80
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret api_key
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret ota_password
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
fast_connect: on
|
||||
|
||||
captive_portal:
|
||||
36
flake.lock
generated
36
flake.lock
generated
@@ -8,11 +8,11 @@
|
||||
},
|
||||
"locked": {
|
||||
"dir": "pkgs/firefox-addons",
|
||||
"lastModified": 1763697825,
|
||||
"narHash": "sha256-AgCCcVPOi1tuzuW5/StlwqBjRWSX62oL97qWuxrq5UA=",
|
||||
"lastModified": 1766762570,
|
||||
"narHash": "sha256-Nevsj5NYurwp3I6nSMeh3uirwoinVSbCldqOXu4smms=",
|
||||
"owner": "rycee",
|
||||
"repo": "nur-expressions",
|
||||
"rev": "cefce78793603231be226fa77e7ad58e0e4899b8",
|
||||
"rev": "03d7d310ea91d6e4b47ed70aa86c781fcc5b38e1",
|
||||
"type": "gitlab"
|
||||
},
|
||||
"original": {
|
||||
@@ -29,11 +29,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1763748372,
|
||||
"narHash": "sha256-AUc78Qv3sWir0hvbmfXoZ7Jzq9VVL97l+sP9Jgms+JU=",
|
||||
"lastModified": 1766682973,
|
||||
"narHash": "sha256-GKO35onS711ThCxwWcfuvbIBKXwriahGqs+WZuJ3v9E=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "d10a9b16b2a3ee28433f3d1c603f4e9f1fecb8e1",
|
||||
"rev": "91cdb0e2d574c64fae80d221f4bf09d5592e9ec2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -44,11 +44,11 @@
|
||||
},
|
||||
"nixos-hardware": {
|
||||
"locked": {
|
||||
"lastModified": 1762847253,
|
||||
"narHash": "sha256-BWWnUUT01lPwCWUvS0p6Px5UOBFeXJ8jR+ZdLX8IbrU=",
|
||||
"lastModified": 1766568855,
|
||||
"narHash": "sha256-UXVtN77D7pzKmzOotFTStgZBqpOcf8cO95FcupWp4Zo=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixos-hardware",
|
||||
"rev": "899dc449bc6428b9ee6b3b8f771ca2b0ef945ab9",
|
||||
"rev": "c5db9569ac9cc70929c268ac461f4003e3e5ca80",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -60,11 +60,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1763421233,
|
||||
"narHash": "sha256-Stk9ZYRkGrnnpyJ4eqt9eQtdFWRRIvMxpNRf4sIegnw=",
|
||||
"lastModified": 1766651565,
|
||||
"narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "89c2b2330e733d6cdb5eae7b899326930c2c0648",
|
||||
"rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -76,11 +76,11 @@
|
||||
},
|
||||
"nixpkgs-master": {
|
||||
"locked": {
|
||||
"lastModified": 1763774007,
|
||||
"narHash": "sha256-PPeHfKA11P09kBkBD5pS3tIAFjnG5muHQnODQGTY87g=",
|
||||
"lastModified": 1766794443,
|
||||
"narHash": "sha256-Q8IyTQ3Lu8vX/iqO3U+E4pjLbP1NsqFih6uElf8OYrQ=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8a7cf7e9e18384533d9ecd0bfbcf475ac1dc497e",
|
||||
"rev": "088b069b8270ee36d83533c86b9f91d924d185d9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -125,11 +125,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1763607916,
|
||||
"narHash": "sha256-VefBA1JWRXM929mBAFohFUtQJLUnEwZ2vmYUNkFnSjE=",
|
||||
"lastModified": 1766289575,
|
||||
"narHash": "sha256-BOKCwOQQIP4p9z8DasT5r+qjri3x7sPCOq+FTjY8Z+o=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "877bb495a6f8faf0d89fc10bd142c4b7ed2bcc0b",
|
||||
"rev": "9836912e37aef546029e48c8749834735a6b9dad",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
# When applied, the stable nixpkgs set (declared in the flake inputs) will be accessible through 'pkgs.stable'
|
||||
stable = final: _prev: {
|
||||
stable = import inputs.nixpkgs-stable {
|
||||
system = final.system;
|
||||
system = final.stdenv.hostPlatform.system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
};
|
||||
# When applied, the master nixpkgs set (declared in the flake inputs) will be accessible through 'pkgs.master'
|
||||
master = final: _prev: {
|
||||
master = import inputs.nixpkgs-master {
|
||||
system = final.system;
|
||||
system = final.stdenv.hostPlatform.system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
"""Safe reboot helper."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Annotated
|
||||
|
||||
import typer
|
||||
|
||||
from python.common import bash_wrapper, configure_logger
|
||||
from python.zfs import Dataset, get_datasets
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Sequence
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_root_pool_datasets(dataset_prefix: str) -> list[Dataset]:
|
||||
"""Return datasets that start with the provided prefix."""
|
||||
return [dataset for dataset in get_datasets() if dataset.name.startswith(dataset_prefix)]
|
||||
|
||||
|
||||
def get_non_executable_datasets(datasets: Sequence[Dataset]) -> list[str]:
|
||||
"""Return dataset names that have exec disabled."""
|
||||
return [dataset.name for dataset in datasets if dataset.exec.lower() != "on"]
|
||||
|
||||
|
||||
def drive_present(drive: str) -> bool:
|
||||
"""Check whether the provided drive exists."""
|
||||
drive_path = drive.strip()
|
||||
if not drive_path:
|
||||
error = "Drive path cannot be empty"
|
||||
raise ValueError(error)
|
||||
|
||||
return Path(drive_path).exists()
|
||||
|
||||
|
||||
def reboot_system() -> None:
|
||||
"""Call systemctl reboot."""
|
||||
output, return_code = bash_wrapper("systemctl reboot")
|
||||
if return_code != 0:
|
||||
raise RuntimeError(output.strip() or "Failed to issue reboot command")
|
||||
|
||||
|
||||
def validate_state(drive: str | None, dataset_prefix: str) -> list[str]:
|
||||
"""Validate dataset and drive state."""
|
||||
datasets = get_root_pool_datasets(dataset_prefix)
|
||||
|
||||
errors: list[str] = []
|
||||
if not datasets:
|
||||
errors.append(f"No datasets found with prefix {dataset_prefix}")
|
||||
else:
|
||||
non_exec_datasets = get_non_executable_datasets(datasets)
|
||||
if non_exec_datasets:
|
||||
errors.append(f"Datasets missing exec=on: {', '.join(non_exec_datasets)}")
|
||||
|
||||
if drive:
|
||||
try:
|
||||
if not drive_present(drive):
|
||||
errors.append(f"Drive {drive} is not present")
|
||||
except ValueError as err:
|
||||
errors.append(str(err))
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def reboot(
|
||||
drive: Annotated[str | None, typer.Argument(help="Drive that must exist before rebooting.")] = None,
|
||||
dataset_prefix: Annotated[
|
||||
str,
|
||||
typer.Option(
|
||||
"--dataset-prefix",
|
||||
"-p",
|
||||
help="Datasets with this prefix are validated.",
|
||||
),
|
||||
] = "root_pool/",
|
||||
dry_run: Annotated[
|
||||
bool,
|
||||
typer.Option(
|
||||
"--check-only",
|
||||
help="Only validate state without issuing the reboot command.",
|
||||
),
|
||||
] = False,
|
||||
) -> None:
|
||||
"""Validate datasets and drive before rebooting."""
|
||||
configure_logger()
|
||||
logger.info("Starting safe reboot checks")
|
||||
|
||||
if errors := validate_state(drive, dataset_prefix):
|
||||
for error in errors:
|
||||
logger.error(error)
|
||||
sys.exit(1)
|
||||
|
||||
if dry_run:
|
||||
logger.info("All checks passed")
|
||||
return
|
||||
|
||||
logger.info("All checks passed, issuing reboot")
|
||||
reboot_system()
|
||||
|
||||
|
||||
def cli() -> None:
|
||||
"""CLI entry point."""
|
||||
typer.run(reboot)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
||||
14
shell.nix
14
shell.nix
@@ -6,14 +6,14 @@
|
||||
default = pkgs.mkShell {
|
||||
NIX_CONFIG = "extra-experimental-features = nix-command flakes ca-derivations";
|
||||
nativeBuildInputs = with pkgs; [
|
||||
nix
|
||||
home-manager
|
||||
git
|
||||
my_python
|
||||
|
||||
ssh-to-age
|
||||
gnupg
|
||||
age
|
||||
busybox
|
||||
git
|
||||
gnupg
|
||||
home-manager
|
||||
my_python
|
||||
nix
|
||||
ssh-to-age
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"Qihoo360-Light-R1-32B"
|
||||
];
|
||||
models = "/zfs/models";
|
||||
acceleration = "cuda";
|
||||
openFirewall = true;
|
||||
};
|
||||
# open-webui = {
|
||||
|
||||
@@ -60,16 +60,20 @@
|
||||
extraPackages =
|
||||
python3Packages: with python3Packages; [
|
||||
aioesphomeapi # for esphome
|
||||
aiounifi # for ubiquiti integration
|
||||
bleak-esphome # for esphome
|
||||
esphome-dashboard-api # for esphome
|
||||
forecast-solar # for solar forecast
|
||||
gtts # not sure what wants this
|
||||
ical # for todo
|
||||
jellyfin-apiclient-python # for jellyfin
|
||||
paho-mqtt # for mqtt
|
||||
psycopg2 # for postgresql
|
||||
py-improv-ble-client # for esphome
|
||||
pymodbus # for modbus
|
||||
pyopenweathermap # for weather
|
||||
uiprotect # for ubiquiti integration
|
||||
unifi-discovery # for ubiquiti integration
|
||||
];
|
||||
extraComponents = [ "isal" ];
|
||||
};
|
||||
|
||||
@@ -1,45 +1,7 @@
|
||||
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
|
||||
source: sensor.batteries_jk0_charging_power
|
||||
name: "JK0 energy in"
|
||||
unique_id: jk0_energy_in_kwh
|
||||
unit_prefix: k
|
||||
@@ -48,7 +10,7 @@ sensor:
|
||||
max_sub_interval:
|
||||
minutes: 5
|
||||
- platform: integration
|
||||
source: sensor.jk0_discharge_power_w
|
||||
source: sensor.batteries_jk0_discharging_power
|
||||
name: "JK0 energy out"
|
||||
unique_id: jk0_energy_out_kwh
|
||||
unit_prefix: k
|
||||
@@ -59,7 +21,7 @@ sensor:
|
||||
|
||||
# Battery 1
|
||||
- platform: integration
|
||||
source: sensor.jk1_charge_power_w
|
||||
source: sensor.battery1_jk1_charging_power
|
||||
name: "JK1 energy in"
|
||||
unique_id: jk1_energy_in_kwh
|
||||
unit_prefix: k
|
||||
@@ -68,7 +30,7 @@ sensor:
|
||||
max_sub_interval:
|
||||
minutes: 5
|
||||
- platform: integration
|
||||
source: sensor.jk1_discharge_power_w
|
||||
source: sensor.battery1_jk1_discharging_power
|
||||
name: "JK1 energy out"
|
||||
unique_id: jk1_energy_out_kwh
|
||||
unit_prefix: k
|
||||
|
||||
@@ -17,7 +17,6 @@ in
|
||||
./services
|
||||
./hardware.nix
|
||||
./networking.nix
|
||||
./nvidia.nix
|
||||
./programs.nix
|
||||
./runners
|
||||
./syncthing.nix
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
let
|
||||
vars = import ../vars.nix;
|
||||
in
|
||||
{
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [
|
||||
6882
|
||||
8081
|
||||
8118
|
||||
];
|
||||
allowedUDPPorts = [ 6882 ];
|
||||
};
|
||||
virtualisation.oci-containers.containers.qbitvpn = {
|
||||
image = "binhex/arch-qbittorrentvpn:5.0.3-1-01";
|
||||
devices = [ "/dev/net/tun:/dev/net/tun" ];
|
||||
extraOptions = [ "--cap-add=NET_ADMIN" ];
|
||||
ports = [
|
||||
"6882:6881"
|
||||
"6882:6881/udp"
|
||||
"8081:8081"
|
||||
"8118:8118"
|
||||
];
|
||||
volumes = [
|
||||
"${vars.docker_configs}/qbitvpn:/config"
|
||||
"${vars.qbitvpn}:/data"
|
||||
"${vars.qbitvpn_scratch}:/data/incomplete"
|
||||
"/etc/localtime:/etc/localtime:ro"
|
||||
];
|
||||
environment = {
|
||||
WEBUI_PORT = "8081";
|
||||
PUID = "600";
|
||||
PGID = "100";
|
||||
VPN_ENABLED = "yes";
|
||||
VPN_CLIENT = "openvpn";
|
||||
STRICT_PORT_FORWARD = "yes";
|
||||
ENABLE_PRIVOXY = "yes";
|
||||
LAN_NETWORK = "192.168.90.0/24";
|
||||
NAME_SERVERS = "1.1.1.1,1.0.0.1";
|
||||
UMASK = "000";
|
||||
DEBUG = "false";
|
||||
DELUGE_DAEMON_LOG_LEVEL = "debug";
|
||||
DELUGE_WEB_LOG_LEVEL = "debug";
|
||||
};
|
||||
environmentFiles = [ "${vars.secrets}/docker/qbitvpn" ];
|
||||
autoStart = true;
|
||||
};
|
||||
}
|
||||
@@ -71,12 +71,6 @@ in
|
||||
"luks-media_pool-nvme-INTEL_SSDPE2ME012T4_CVMD5130000U1P2HGN-part1" =
|
||||
makeLuksSSD "/dev/disk/by-id/nvme-INTEL_SSDPE2ME012T4_CVMD5130000U1P2HGN-part1";
|
||||
|
||||
# Scratch pool
|
||||
"luks-scratch-pool-ata-CT480BX500SSD1_2314E6C3C01C-part1" =
|
||||
makeLuksSSD "/dev/disk/by-id/ata-CT480BX500SSD1_2314E6C3C01C-part1";
|
||||
"luks-scratch-pool-ata-CT480BX500SSD1_2314E6C3C01E-part1" =
|
||||
makeLuksSSD "/dev/disk/by-id/ata-CT480BX500SSD1_2314E6C3C01E-part1";
|
||||
|
||||
# Storage pool
|
||||
"luks-storage_pool-nvme-Samsung_SSD_970_EVO_Plus_2TB_S6S2NS0T834822N-part1" =
|
||||
makeLuksSSD "/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_Plus_2TB_S6S2NS0T834822N-part1";
|
||||
|
||||
@@ -2,31 +2,71 @@
|
||||
networking = {
|
||||
hostName = "jeeves";
|
||||
hostId = "0e15ce35";
|
||||
firewall.enable = true;
|
||||
firewall = {
|
||||
enable = true;
|
||||
interfaces.br-nix-builder = {
|
||||
allowedTCPPorts = [ ];
|
||||
allowedUDPPorts = [ ];
|
||||
};
|
||||
};
|
||||
useNetworkd = true;
|
||||
};
|
||||
|
||||
systemd.network = {
|
||||
enable = true;
|
||||
wait-online = {
|
||||
enable = false;
|
||||
anyInterface = true;
|
||||
};
|
||||
netdevs = {
|
||||
"20-br-nix-builder" = {
|
||||
netdevConfig = {
|
||||
Kind = "bridge";
|
||||
Name = "br-nix-builder";
|
||||
};
|
||||
};
|
||||
"30-internet-vlan" = {
|
||||
netdevConfig = {
|
||||
Kind = "vlan";
|
||||
Name = "internet-vlan";
|
||||
};
|
||||
vlanConfig.Id = 100;
|
||||
};
|
||||
};
|
||||
networks = {
|
||||
"10-1GB_Primary" = {
|
||||
matchConfig.Name = "enp98s0f0";
|
||||
matchConfig.Name = "enp97s0f1";
|
||||
address = [ "192.168.99.14/24" ];
|
||||
routes = [ { Gateway = "192.168.99.1"; } ];
|
||||
vlan = [ "internet-vlan" ];
|
||||
linkConfig.RequiredForOnline = "routable";
|
||||
};
|
||||
"10-1GB_Secondary" = {
|
||||
matchConfig.Name = "enp98s0f1";
|
||||
DHCP = "yes";
|
||||
"50-internet-vlan" = {
|
||||
matchConfig.Name = "internet-vlan";
|
||||
bridge = [ "br-nix-builder" ];
|
||||
linkConfig.RequiredForOnline = "no";
|
||||
};
|
||||
"10-10GB_Primary" = {
|
||||
matchConfig.Name = "enp97s0f0np0";
|
||||
DHCP = "yes";
|
||||
linkConfig.RequiredForOnline = "routable";
|
||||
};
|
||||
"10-10GB_Secondary" = {
|
||||
matchConfig.Name = "enp97s0f1np1";
|
||||
DHCP = "yes";
|
||||
"60-br-nix-builder" = {
|
||||
matchConfig.Name = "br-nix-builder";
|
||||
bridgeConfig = { };
|
||||
address = [ "192.168.3.10/24" ];
|
||||
routingPolicyRules = [
|
||||
{
|
||||
From = "192.168.3.0/24";
|
||||
Table = 100;
|
||||
Priority = 100;
|
||||
}
|
||||
];
|
||||
routes = [
|
||||
{
|
||||
Gateway = "192.168.3.1";
|
||||
Table = 100;
|
||||
GatewayOnLink = false;
|
||||
Metric = 2048;
|
||||
PreferredSource = "192.168.3.10";
|
||||
}
|
||||
];
|
||||
linkConfig.RequiredForOnline = "no";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
nixpkgs.config.cudaSupport = true;
|
||||
|
||||
services.xserver.videoDrivers = [ "nvidia" ];
|
||||
hardware = {
|
||||
nvidia = {
|
||||
modesetting.enable = true;
|
||||
powerManagement.enable = true;
|
||||
package = config.boot.kernelPackages.nvidiaPackages.beta;
|
||||
nvidiaSettings = true;
|
||||
open = false;
|
||||
};
|
||||
nvidia-container-toolkit.enable = true;
|
||||
};
|
||||
}
|
||||
@@ -16,11 +16,20 @@
|
||||
};
|
||||
|
||||
services.nix_builder.containers = {
|
||||
nix-builder-0.enable = true;
|
||||
nix-builder-1.enable = true;
|
||||
nix-builder-2.enable = true;
|
||||
nix-builder-3.enable = true;
|
||||
nix-builder-4.enable = true;
|
||||
nix-builder-5.enable = true;
|
||||
nix-builder-00.enable = true;
|
||||
nix-builder-01.enable = true;
|
||||
nix-builder-02.enable = true;
|
||||
nix-builder-03.enable = true;
|
||||
nix-builder-04.enable = true;
|
||||
nix-builder-05.enable = true;
|
||||
nix-builder-06.enable = true;
|
||||
nix-builder-07.enable = true;
|
||||
nix-builder-08.enable = true;
|
||||
nix-builder-09.enable = true;
|
||||
nix-builder-10.enable = true;
|
||||
nix-builder-11.enable = true;
|
||||
nix-builder-12.enable = true;
|
||||
nix-builder-13.enable = true;
|
||||
nix-builder-14.enable = true;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,106 +6,131 @@
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
vars = import ../vars.nix;
|
||||
cfg = config.services.nix_builder;
|
||||
in
|
||||
{
|
||||
options.services.nix_builder.containers = mkOption {
|
||||
type = types.attrsOf (
|
||||
types.submodule (
|
||||
{ name, ... }:
|
||||
{
|
||||
options.enable = mkEnableOption "GitHub runner container";
|
||||
}
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
description = "GitHub runner container configurations";
|
||||
options.services.nix_builder = {
|
||||
bridgeName = mkOption {
|
||||
type = types.str;
|
||||
default = "br-nix-builder";
|
||||
description = "Bridge name for the builder containers.";
|
||||
};
|
||||
|
||||
containers = mkOption {
|
||||
type = types.attrsOf (
|
||||
types.submodule (
|
||||
{ name, ... }:
|
||||
{
|
||||
options.enable = mkEnableOption "GitHub runner container";
|
||||
}
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
description = "GitHub runner container configurations";
|
||||
};
|
||||
};
|
||||
|
||||
config.containers = mapAttrs (
|
||||
name: cfg:
|
||||
mkIf cfg.enable {
|
||||
autoStart = true;
|
||||
bindMounts = {
|
||||
"/storage" = {
|
||||
mountPoint = "/zfs/media/github-runners/${name}";
|
||||
isReadOnly = false;
|
||||
config = {
|
||||
containers = mapAttrs (
|
||||
name: containerCfg:
|
||||
mkIf containerCfg.enable {
|
||||
autoStart = true;
|
||||
privateNetwork = true;
|
||||
hostBridge = cfg.bridgeName;
|
||||
ephemeral = true;
|
||||
bindMounts = {
|
||||
storage = {
|
||||
hostPath = "/zfs/media/github-runners/${name}";
|
||||
mountPoint = "/zfs/media/github-runners/${name}";
|
||||
isReadOnly = false;
|
||||
};
|
||||
host-nix = {
|
||||
mountPoint = "/host-nix/var/nix/daemon-socket";
|
||||
hostPath = "/nix/var/nix/daemon-socket";
|
||||
isReadOnly = false;
|
||||
};
|
||||
pat = {
|
||||
hostPath = "${vars.secrets}/services/github-runners/runner_pat";
|
||||
mountPoint = "${vars.secrets}/services/github-runners/runner_pat";
|
||||
isReadOnly = true;
|
||||
};
|
||||
};
|
||||
"/secrets".mountPoint = "${vars.secrets}/services/github-runners/${name}";
|
||||
"ssh-keys".mountPoint = "${vars.secrets}/services/github-runners/id_ed25519_github-runners";
|
||||
};
|
||||
config =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
nix.settings = {
|
||||
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="
|
||||
];
|
||||
experimental-features = [
|
||||
"flakes"
|
||||
"nix-command"
|
||||
];
|
||||
};
|
||||
programs.ssh.extraConfig = ''
|
||||
Host jeeves
|
||||
Port 629
|
||||
User github-runners
|
||||
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;
|
||||
workDir = "/zfs/media/github-runners/${name}";
|
||||
url = "https://github.com/RichieCahill/dotfiles";
|
||||
extraLabels = [ "nixos" ];
|
||||
tokenFile = "${vars.secrets}/services/github-runners/${name}";
|
||||
user = "github-runners";
|
||||
group = "github-runners";
|
||||
extraPackages = with pkgs; [
|
||||
nixfmt-rfc-style
|
||||
nixos-rebuild
|
||||
openssh
|
||||
treefmt
|
||||
my_python
|
||||
];
|
||||
};
|
||||
users = {
|
||||
users.github-runners = {
|
||||
shell = pkgs.bash;
|
||||
isSystemUser = true;
|
||||
group = "github-runners";
|
||||
uid = 601;
|
||||
config =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
networking = {
|
||||
useDHCP = lib.mkDefault true;
|
||||
interfaces.eth0.useDHCP = true;
|
||||
# Ensure containers don't inherit the host's stub resolver (127.0.0.53) which was causing issues
|
||||
useHostResolvConf = false;
|
||||
};
|
||||
groups.github-runners.gid = 601;
|
||||
nix.settings = {
|
||||
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="
|
||||
];
|
||||
experimental-features = [
|
||||
"flakes"
|
||||
"nix-command"
|
||||
];
|
||||
sandbox = true;
|
||||
allowed-users = [ "github-runners" ];
|
||||
trusted-users = [
|
||||
"root"
|
||||
"github-runners"
|
||||
];
|
||||
};
|
||||
nixpkgs = {
|
||||
overlays = builtins.attrValues outputs.overlays;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
services.github-runners.${name} = {
|
||||
enable = true;
|
||||
replace = true;
|
||||
workDir = "/zfs/media/github-runners/${name}";
|
||||
url = "https://github.com/RichieCahill/dotfiles";
|
||||
extraLabels = [ "nixos" ];
|
||||
tokenFile = "${vars.secrets}/services/github-runners/runner_pat";
|
||||
user = "github-runners";
|
||||
group = "github-runners";
|
||||
extraPackages = with pkgs; [
|
||||
busybox
|
||||
nixfmt-rfc-style
|
||||
nixos-rebuild
|
||||
treefmt
|
||||
my_python
|
||||
];
|
||||
};
|
||||
users = {
|
||||
users.github-runners = {
|
||||
shell = pkgs.bash;
|
||||
isSystemUser = true;
|
||||
group = "github-runners";
|
||||
uid = 601;
|
||||
};
|
||||
groups.github-runners.gid = 601;
|
||||
};
|
||||
system.stateVersion = "24.05";
|
||||
};
|
||||
system.stateVersion = "24.11";
|
||||
};
|
||||
}
|
||||
) config.services.nix_builder.containers;
|
||||
}
|
||||
) cfg.containers;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ sudo zpool add storage -o ashift=12 special mirror
|
||||
sudo zpool add storage -o ashift=12 logs mirror
|
||||
|
||||
# scratch
|
||||
sudo zpool create -o ashift=12 -O acltype=posixacl -O atime=off -O dnodesize=auto -O xattr=sa -O compression=zstd -m /zfs/scratch scratch
|
||||
sudo zpool create scratch -o ashift=12 -O acltype=posixacl -O atime=off -O dnodesize=auto -O xattr=sa -O compression=zstd -O encryption=aes-256-gcm -O keyformat=hex -O keylocation=file:///key -m /zfs/scratch
|
||||
|
||||
# media datasets
|
||||
sudo zfs create -o compression=zstd-9 media/docker
|
||||
@@ -25,9 +25,8 @@ sudo zfs create -o exec=off media/share
|
||||
sudo zfs create -o recordsize=16k -o primarycache=metadata -o mountpoint=/zfs/media/database/postgres media/postgres
|
||||
|
||||
# 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
|
||||
sudo zfs create scratch/kafka -o mountpoint=/zfs/scratch/kafka -o recordsize=1M
|
||||
sudo zfs create scratch/transmission -o mountpoint=/zfs/scratch/transmission -o recordsize=16k -o sync=disabled
|
||||
|
||||
# storage datasets
|
||||
sudo zfs create -o recordsize=1M -o compression=zstd-19 storage/archive
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
vars = import ../vars.nix;
|
||||
in
|
||||
{
|
||||
systemd.services.filebrowser = {
|
||||
description = "filebrowser";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = "richie";
|
||||
Group = "users";
|
||||
ExecStart = "${pkgs.filebrowser}/bin/filebrowser --root=/zfs --address=0.0.0.0 --database=${vars.docker_configs}/filebrowser/filebrowser.db";
|
||||
Restart = "on-failure";
|
||||
};
|
||||
};
|
||||
}
|
||||
46
systems/jeeves/services/gitea.nix
Normal file
46
systems/jeeves/services/gitea.nix
Normal file
@@ -0,0 +1,46 @@
|
||||
let
|
||||
vars = import ../vars.nix;
|
||||
in
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = [ 6443 ];
|
||||
|
||||
services.gitea = {
|
||||
enable = true;
|
||||
appName = "TMM Workshop";
|
||||
stateDir = "${vars.services}/gitea/";
|
||||
lfs.enable = true;
|
||||
database = {
|
||||
type = "postgres";
|
||||
name = "gitea";
|
||||
user = "gitea";
|
||||
socket = "/run/postgresql";
|
||||
port = 5432;
|
||||
createDatabase = false;
|
||||
};
|
||||
settings = {
|
||||
service.DISABLE_REGISTRATION = true;
|
||||
server = {
|
||||
DOMAIN = "tmmworkshop.com";
|
||||
ROOT_URL = "https://gitea.tmmworkshop.com/";
|
||||
HTTP_PORT = 6443;
|
||||
SSH_PORT = 2223;
|
||||
SSH_LISTEN_PORT = 2224;
|
||||
START_SSH_SERVER = true;
|
||||
PUBLIC_URL_DETECTION = "auto";
|
||||
};
|
||||
repository = {
|
||||
ENABLE_PUSH_CREATE_USER = true;
|
||||
DEFAULT_MERGE_STYLE = "rebase-merge";
|
||||
};
|
||||
log = {
|
||||
LEVEL = "Trace";
|
||||
ENABLE_SSH_LOG = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.gitea = {
|
||||
requires = [ "docker.service" ];
|
||||
after = [ "docker.service" ];
|
||||
};
|
||||
}
|
||||
@@ -27,21 +27,21 @@ frontend ContentSwitching
|
||||
# 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
|
||||
acl host_gitea hdr(host) -i gitea.tmmworkshop.com
|
||||
|
||||
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
|
||||
use_backend gitea if host_gitea
|
||||
|
||||
backend audiobookshelf_nodes
|
||||
mode http
|
||||
@@ -51,10 +51,6 @@ backend cache_nodes
|
||||
mode http
|
||||
server server 127.0.0.1:5000
|
||||
|
||||
backend filebrowser_nodes
|
||||
mode http
|
||||
server server 127.0.0.1:8080
|
||||
|
||||
backend homeassistant_nodes
|
||||
mode http
|
||||
server server 192.168.90.35:8123
|
||||
@@ -77,3 +73,7 @@ backend gcw_nodes
|
||||
backend n8n
|
||||
mode http
|
||||
server server 127.0.0.1:5678
|
||||
|
||||
backend gitea
|
||||
mode http
|
||||
server server 127.0.0.1:6443
|
||||
@@ -28,13 +28,7 @@ in
|
||||
|
||||
#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
|
||||
local gitea gitea trust
|
||||
|
||||
# megan
|
||||
host megan megan 192.168.90.1/24 trust
|
||||
@@ -96,6 +90,16 @@ in
|
||||
replication = true;
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "gitea";
|
||||
ensureDBOwnership = true;
|
||||
ensureClauses = {
|
||||
login = true;
|
||||
createrole = true;
|
||||
createdb = true;
|
||||
replication = true;
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "megan";
|
||||
ensureDBOwnership = true;
|
||||
@@ -130,6 +134,7 @@ in
|
||||
ensureDatabases = [
|
||||
"gcw"
|
||||
"hass"
|
||||
"gitea"
|
||||
"math"
|
||||
"megan"
|
||||
"mxr_dev"
|
||||
|
||||
@@ -3,9 +3,7 @@ services = [
|
||||
"audiobookshelf",
|
||||
"cloud_flare_tunnel",
|
||||
"haproxy",
|
||||
"docker-qbitvpn",
|
||||
"docker",
|
||||
"filebrowser",
|
||||
"home-assistant",
|
||||
"jellyfin",
|
||||
]
|
||||
|
||||
@@ -64,24 +64,12 @@ 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
|
||||
|
||||
@@ -10,8 +10,6 @@ in
|
||||
docker_configs = "${zfs_media}/docker/configs";
|
||||
home_assistant = "${zfs_media}/home_assistant";
|
||||
notes = "${zfs_media}/notes";
|
||||
qbitvpn = "${zfs_storage}/qbitvpn";
|
||||
qbitvpn_scratch = "${zfs_scratch}/qbitvpn";
|
||||
secrets = "${zfs_storage}/secrets";
|
||||
services = "${zfs_media}/services";
|
||||
share = "${zfs_media}/share";
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
./hardware.nix
|
||||
./llms.nix
|
||||
./syncthing.nix
|
||||
./qmk.nix
|
||||
inputs.nixos-hardware.nixosModules.framework-13-7040-amd
|
||||
];
|
||||
|
||||
|
||||
34
systems/rhapsody-in-green/qmk.nix
Normal file
34
systems/rhapsody-in-green/qmk.nix
Normal file
@@ -0,0 +1,34 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
environment.systemPackages = with pkgs; [
|
||||
qmk
|
||||
vial
|
||||
];
|
||||
|
||||
services = {
|
||||
udev = {
|
||||
packages = [ pkgs.qmk-udev-rules ];
|
||||
extraRules = ''
|
||||
# Keychron keyboards
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="3434", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="3434", MODE="0660", GROUP="plugdev"
|
||||
|
||||
# Some boards use 32f0 as vendor id
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="32f0", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="32f0", MODE="0660", GROUP="plugdev"
|
||||
|
||||
# Keychron HID device permissions
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="3434", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="3434", MODE="0660", GROUP="plugdev"
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="32f0", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="32f0", MODE="0660", GROUP="plugdev"
|
||||
|
||||
|
||||
# Keychron / QMK common bootloaders
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="df11", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="03eb", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="16c0", MODE="0660", GROUP="plugdev"
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -57,5 +57,5 @@ 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 stdout == "ls: /this/path/does/not/exist: No such file or directory\n"
|
||||
assert returncode == expected_error
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
"""Tests for safe_reboot."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import pytest
|
||||
|
||||
from python.tools.safe_reboot import reboot
|
||||
from python.zfs.dataset import Dataset
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
SAFE_REBOOT = "python.tools.safe_reboot"
|
||||
|
||||
|
||||
def create_dataset(mocker: MockerFixture, name: str, exec_state: str) -> Dataset:
|
||||
"""Create a mock dataset."""
|
||||
dataset = mocker.MagicMock(spec=Dataset)
|
||||
dataset.name = name
|
||||
dataset.exec = exec_state
|
||||
return dataset
|
||||
|
||||
|
||||
def test_reboot_reboots_when_checks_pass(mocker: MockerFixture) -> None:
|
||||
"""The command should reboot when all checks pass."""
|
||||
dataset = create_dataset(mocker, "root_pool/root", "on")
|
||||
mocker.patch(f"{SAFE_REBOOT}.get_datasets", return_value=(dataset,))
|
||||
mocker.patch(f"{SAFE_REBOOT}.drive_present", return_value=True)
|
||||
mock_bash = mocker.patch(f"{SAFE_REBOOT}.bash_wrapper", return_value=("", 0))
|
||||
|
||||
reboot("/dev/disk/root-drive")
|
||||
|
||||
mock_bash.assert_called_once_with("systemctl reboot")
|
||||
|
||||
|
||||
def test_reboot_reboots_without_drive_requirement(mocker: MockerFixture) -> None:
|
||||
"""The command should reboot even when no drive is provided."""
|
||||
dataset = create_dataset(mocker, "root_pool/root", "on")
|
||||
mocker.patch(f"{SAFE_REBOOT}.get_datasets", return_value=(dataset,))
|
||||
mock_bash = mocker.patch(f"{SAFE_REBOOT}.bash_wrapper", return_value=("", 0))
|
||||
|
||||
reboot(None)
|
||||
|
||||
mock_bash.assert_called_once_with("systemctl reboot")
|
||||
|
||||
|
||||
def test_reboot_errors_on_non_exec_dataset(mocker: MockerFixture) -> None:
|
||||
"""The command should exit when a dataset lacks exec."""
|
||||
dataset = create_dataset(mocker, "root_pool/root", "off")
|
||||
mocker.patch(f"{SAFE_REBOOT}.get_datasets", return_value=(dataset,))
|
||||
mocker.patch(f"{SAFE_REBOOT}.drive_present", return_value=True)
|
||||
mocker.patch(f"{SAFE_REBOOT}.bash_wrapper", return_value=("", 0))
|
||||
|
||||
with pytest.raises(SystemExit) as excinfo:
|
||||
reboot("/dev/disk/root-drive")
|
||||
|
||||
assert excinfo.value.code == 1
|
||||
|
||||
|
||||
def test_reboot_errors_when_driver_missing(mocker: MockerFixture) -> None:
|
||||
"""The command should exit when the requested driver is absent."""
|
||||
dataset = create_dataset(mocker, "root_pool/root", "on")
|
||||
mocker.patch(f"{SAFE_REBOOT}.get_datasets", return_value=(dataset,))
|
||||
mocker.patch(f"{SAFE_REBOOT}.drive_present", return_value=False)
|
||||
mocker.patch(f"{SAFE_REBOOT}.bash_wrapper", return_value=("", 0))
|
||||
|
||||
with pytest.raises(SystemExit) as excinfo:
|
||||
reboot("/dev/disk/root-drive")
|
||||
|
||||
assert excinfo.value.code == 1
|
||||
|
||||
|
||||
def test_reboot_errors_when_no_datasets_found(mocker: MockerFixture) -> None:
|
||||
"""The command should exit when no datasets match the prefix."""
|
||||
mocker.patch(f"{SAFE_REBOOT}.get_datasets", return_value=())
|
||||
mocker.patch(f"{SAFE_REBOOT}.drive_present", return_value=True)
|
||||
mocker.patch(f"{SAFE_REBOOT}.bash_wrapper", return_value=("", 0))
|
||||
|
||||
with pytest.raises(SystemExit) as excinfo:
|
||||
reboot("/dev/disk/root-drive")
|
||||
|
||||
assert excinfo.value.code == 1
|
||||
|
||||
|
||||
def test_reboot_check_only_skips_reboot(mocker: MockerFixture) -> None:
|
||||
"""The command should only validate when --check-only is provided."""
|
||||
dataset = create_dataset(mocker, "root_pool/root", "on")
|
||||
mocker.patch(f"{SAFE_REBOOT}.get_datasets", return_value=(dataset,))
|
||||
mocker.patch(f"{SAFE_REBOOT}.drive_present", return_value=True)
|
||||
mock_bash = mocker.patch(f"{SAFE_REBOOT}.bash_wrapper", return_value=("", 0))
|
||||
|
||||
reboot("/dev/disk/root-drive", check_only=True)
|
||||
|
||||
mock_bash.assert_not_called()
|
||||
@@ -41,6 +41,7 @@ in
|
||||
"scanner"
|
||||
"transmission"
|
||||
"uaccess"
|
||||
"uucp"
|
||||
"wireshark"
|
||||
];
|
||||
uid = 1000;
|
||||
|
||||
@@ -9,23 +9,24 @@
|
||||
home.packages = with pkgs; [
|
||||
candy-icons
|
||||
chromium
|
||||
discord-canary
|
||||
gimp
|
||||
gparted
|
||||
jetbrains.datagrip
|
||||
mediainfo
|
||||
nemo
|
||||
nemo-fileroller
|
||||
obs-studio
|
||||
obsidian
|
||||
prismlauncher
|
||||
proxychains
|
||||
prusa-slicer
|
||||
signal-desktop
|
||||
sweet-nova
|
||||
util-linux
|
||||
vlc
|
||||
# comms
|
||||
discord-canary
|
||||
signal-desktop
|
||||
zoom-us
|
||||
# dev tools
|
||||
jetbrains.datagrip
|
||||
proxychains
|
||||
master.antigravity-fhs
|
||||
gparted
|
||||
# games
|
||||
dwarf-fortress
|
||||
tower-pixel-dungeon
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
# cli
|
||||
bat
|
||||
btop
|
||||
busybox
|
||||
eza
|
||||
fd
|
||||
ffmpegthumbnailer
|
||||
@@ -21,7 +22,6 @@
|
||||
ripgrep
|
||||
starship
|
||||
tmux
|
||||
unzip
|
||||
yazi
|
||||
zoxide
|
||||
# Home Assistant
|
||||
@@ -31,13 +31,11 @@
|
||||
# system info
|
||||
hwloc
|
||||
lynis
|
||||
pciutils
|
||||
smartmontools
|
||||
usbutils
|
||||
# networking
|
||||
iperf3
|
||||
nmap
|
||||
wget
|
||||
# python
|
||||
poetry
|
||||
ruff
|
||||
|
||||
Reference in New Issue
Block a user