diff --git a/.github/workflows/flake-check.yml b/.github/workflows/flake-check.yml deleted file mode 100644 index ec98aa5..0000000 --- a/.github/workflows/flake-check.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Flake Check - -on: - push: - branches: [main] - pull_request: - branches: [main] - types: [opened, synchronize, ready_for_review] - -jobs: - check: - runs-on: ubuntu-latest - if: github.event.pull_request.draft == false - steps: - - uses: actions/checkout@v4 - - - uses: cachix/install-nix-action@v24 - with: - nix_path: nixpkgs=channel:nixos-unstable - - - name: Check Root Flake - run: nix flake check - - - name: Check Template Flake - run: | - cd template - sed -i 's|url = "github:ClementBobin/FlakeHypr"|url = "path:./"|' flake.nix - nix flake check - git checkout flake.nix - rm -f flake.lock - \ No newline at end of file diff --git a/.github/workflows/lint-commits.yml b/.github/workflows/lint-commits.yml deleted file mode 100644 index db4f5a7..0000000 --- a/.github/workflows/lint-commits.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Lint Commit Messages -on: - pull_request: - types: [opened, synchronize, ready_for_review] -jobs: - commitlint: - runs-on: ubuntu-latest - if: github.event.pull_request.draft == false - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: cachix/install-nix-action@v24 - with: - nix_path: nixpkgs=channel:nixos-unstable - - - name: Copy commitlint config - run: cp .commitlintrc.json $HOME/.commitlintrc.json - - - name: Validate PR commits - run: nix-shell -p commitlint --run "commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose" - \ No newline at end of file diff --git a/TODO.md b/TODO.md index 1f2118b..9a9e815 100755 --- a/TODO.md +++ b/TODO.md @@ -9,14 +9,19 @@ - VMs: [hostname]-vm (fern-vm, oak-vm, cedar-vm) - [x] hydenix: some yubikey touch detection for hyprlock and waybar -- [?] bluemail +- [x] bluemail - [x] scalar - [x] rust - [x] ClamAV UI - [ ] Navi default nix template - [x] find replacement for diskonaut -- [?] Flutter +- [x] Flutter - [x] create own module for act-github - [ ] Update flake - [ ] Test workflows - [x] Prisma module +- [x] Matrix Server +- [x] Deskflow +- [x] ping module +- [x] db module +- [x] nginx module diff --git a/flake.lock b/flake.lock index 7f76bd7..b37dd76 100755 --- a/flake.lock +++ b/flake.lock @@ -1,80 +1,73 @@ { "nodes": { - "chaotic": { - "inputs": { - "fenix": "fenix", - "flake-schemas": "flake-schemas", - "home-manager": "home-manager", - "jovian": "jovian", - "nixpkgs": "nixpkgs" - }, - "locked": { - "lastModified": 1749757487, - "narHash": "sha256-yPxNQ2wo4Iiv/QiacFcL4gPC/uansnY6FmPvW53fUhg=", - "owner": "chaotic-cx", - "repo": "nyx", - "rev": "12314126ea2c78758055c1f413182fbea617801c", - "type": "github" - }, - "original": { - "owner": "chaotic-cx", - "ref": "nyxpkgs-unstable", - "repo": "nyx", - "type": "github" - } - }, - "deploy-rs": { + "aquamarine": { "inputs": { - "flake-compat": "flake-compat", + "hyprutils": [ + "hydenix", + "hyprland", + "hyprutils" + ], + "hyprwayland-scanner": [ + "hydenix", + "hyprland", + "hyprwayland-scanner" + ], "nixpkgs": [ + "hydenix", + "hyprland", "nixpkgs" ], - "utils": "utils" + "systems": [ + "hydenix", + "hyprland", + "systems" + ] }, "locked": { - "lastModified": 1749105467, - "narHash": "sha256-hXh76y/wDl15almBcqvjryB50B0BaiXJKk20f314RoE=", - "owner": "serokell", - "repo": "deploy-rs", - "rev": "6bc76b872374845ba9d645a2f012b764fecd765f", + "lastModified": 1755946532, + "narHash": "sha256-POePremlUY5GyA1zfbtic6XLxDaQcqHN6l+bIxdT5gc=", + "owner": "hyprwm", + "repo": "aquamarine", + "rev": "81584dae2df6ac79f6b6dae0ecb7705e95129ada", "type": "github" }, "original": { - "owner": "serokell", - "repo": "deploy-rs", + "owner": "hyprwm", + "repo": "aquamarine", "type": "github" } }, - "fenix": { + "chaotic": { "inputs": { - "nixpkgs": [ - "chaotic", - "nixpkgs" - ], - "rust-analyzer-src": "rust-analyzer-src" + "flake-schemas": "flake-schemas", + "home-manager": "home-manager", + "jovian": "jovian", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1737009074, - "narHash": "sha256-BF+OxooVz4KeSIJyLXCnX2VTMsyfGrpEsay76hGRyrI=", - "owner": "nix-community", - "repo": "fenix", - "rev": "fe17a42e2c306d1a8a4fb3f977b12048d8cf7471", + "lastModified": 1756606761, + "narHash": "sha256-lcHMwq0LVcS1mP9o0pq00Von8PsXMsFPPo3ZXGWa7DU=", + "owner": "chaotic-cx", + "repo": "nyx", + "rev": "9e9e58125b4ba190658235106858f9733b25a1b4", "type": "github" }, "original": { - "owner": "nix-community", - "repo": "fenix", + "owner": "chaotic-cx", + "ref": "nyxpkgs-unstable", + "repo": "nyx", "type": "github" } }, "flake-compat": { "flake": false, "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", "owner": "edolstra", "repo": "flake-compat", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", "type": "github" }, "original": { @@ -88,11 +81,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1743550720, - "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "lastModified": 1754487366, + "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18", "type": "github" }, "original": { @@ -115,6 +108,47 @@ "url": "https://flakehub.com/f/DeterminateSystems/flake-schemas/%3D0.1.5.tar.gz" } }, + "flake-utils": { + "inputs": { + "systems": "systems_3" + }, + "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" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "hydenix", + "hyprland", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -123,11 +157,11 @@ ] }, "locked": { - "lastModified": 1736883540, - "narHash": "sha256-dgPgoPUSg8cGAMqbhQRkww665sZtgzpWXxWjlyqhv94=", + "lastModified": 1756261190, + "narHash": "sha256-eiy0klFK5EVJLNilutR7grsZN/7Itj9DyD75eyOf83k=", "owner": "nix-community", "repo": "home-manager", - "rev": "0dfec9deb275854a56c97c356c40ef72e3a2e632", + "rev": "77f348da3176dc68b20a73dab94852a417daf361", "type": "github" }, "original": { @@ -144,109 +178,577 @@ ] }, "locked": { - "lastModified": 1742670145, - "narHash": "sha256-xQ2F9f+ICAGBp/nNv3ddD2U4ZvzuLOci0u/5lyMXPvk=", + "lastModified": 1756261190, + "narHash": "sha256-eiy0klFK5EVJLNilutR7grsZN/7Itj9DyD75eyOf83k=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "77f348da3176dc68b20a73dab94852a417daf361", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "home-manager_3": { + "inputs": { + "nixpkgs": [ + "nix-podman-stacks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1755928099, + "narHash": "sha256-OILVkfhRCm8u18IZ2DKR8gz8CVZM2ZcJmQBXmjFLIfk=", "owner": "nix-community", "repo": "home-manager", - "rev": "63e77d09a133ac641a0c204e7cfb0c97e133706d", + "rev": "4a44fb9f7555da362af9d499817084f4288a957f", "type": "github" }, "original": { "owner": "nix-community", + "ref": "release-25.05", "repo": "home-manager", "type": "github" } }, + "hyde": { + "flake": false, + "locked": { + "lastModified": 1752889605, + "narHash": "sha256-hT7xLsfqsVwcYS/YscRwVOPqK8ZpubhGdpK58INkdJ0=", + "owner": "HyDE-Project", + "repo": "HyDE", + "rev": "168287f2f10e95a4a9a623c955c7e08147c48472", + "type": "github" + }, + "original": { + "owner": "HyDE-Project", + "repo": "HyDE", + "rev": "168287f2f10e95a4a9a623c955c7e08147c48472", + "type": "github" + } + }, "hydenix": { "inputs": { "home-manager": "home-manager_2", + "hyde": "hyde", "hydenix-nixpkgs": "hydenix-nixpkgs", + "hypridle": "hypridle", + "hyprland": "hyprland", + "nix-index-database": "nix-index-database", "nixos-hardware": "nixos-hardware" }, "locked": { - "lastModified": 1743370967, - "narHash": "sha256-GYP0EprbMD1bARQUijYyL93hyxJebS5sPZC13D5HGrE=", + "lastModified": 1755961878, + "narHash": "sha256-t6M3TU5+4ZzqUSEk+KBv0mREtQ3UiR933YNRkDMHWBM=", "owner": "richen604", "repo": "hydenix", - "rev": "174f11d016c18724f5357cfba6ba2b06d466db4c", + "rev": "9836f27996b1eee38a6da3a04ad783943941bbce", "type": "github" }, "original": { "owner": "richen604", + "ref": "v4.9.0", "repo": "hydenix", "type": "github" } }, "hydenix-nixpkgs": { "locked": { - "lastModified": 1742288794, - "narHash": "sha256-Txwa5uO+qpQXrNG4eumPSD+hHzzYi/CdaM80M9XRLCo=", + "lastModified": 1750122687, + "narHash": "sha256-zcGClfkXh4pckf4aGOZ18GFv73n1xHbdMWl17cPLouE=", "owner": "nixos", "repo": "nixpkgs", - "rev": "b6eaf97c6960d97350c584de1b6dcff03c9daf42", + "rev": "c539ae8d21e49776966d714f82fba33b1fca78bc", "type": "github" }, "original": { "owner": "nixos", "repo": "nixpkgs", - "rev": "b6eaf97c6960d97350c584de1b6dcff03c9daf42", + "rev": "c539ae8d21e49776966d714f82fba33b1fca78bc", "type": "github" } }, - "jovian": { + "hyprcursor": { "inputs": { - "nix-github-actions": "nix-github-actions", + "hyprlang": [ + "hydenix", + "hyprland", + "hyprlang" + ], "nixpkgs": [ - "chaotic", + "hydenix", + "hyprland", "nixpkgs" + ], + "systems": [ + "hydenix", + "hyprland", + "systems" ] }, "locked": { - "lastModified": 1737007397, - "narHash": "sha256-uJ7Lk0moWSn8Tr0QkUbOCWR7WrhiLeha4E00rckhl8I=", - "owner": "Jovian-Experiments", - "repo": "Jovian-NixOS", - "rev": "6bbb19666e753c18ef8af35f590cbc3ba42dd0ca", + "lastModified": 1753964049, + "narHash": "sha256-lIqabfBY7z/OANxHoPeIrDJrFyYy9jAM4GQLzZ2feCM=", + "owner": "hyprwm", + "repo": "hyprcursor", + "rev": "44e91d467bdad8dcf8bbd2ac7cf49972540980a5", "type": "github" }, "original": { - "owner": "Jovian-Experiments", - "repo": "Jovian-NixOS", + "owner": "hyprwm", + "repo": "hyprcursor", "type": "github" } }, - "nix-darwin": { + "hyprgraphics": { "inputs": { + "hyprutils": [ + "hydenix", + "hyprland", + "hyprutils" + ], "nixpkgs": [ + "hydenix", + "hyprland", "nixpkgs" + ], + "systems": [ + "hydenix", + "hyprland", + "systems" ] }, "locked": { - "lastModified": 1750618568, - "narHash": "sha256-w9EG5FOXrjXGfbqCcQg9x1lMnTwzNDW5BMXp8ddy15E=", - "owner": "nix-darwin", - "repo": "nix-darwin", - "rev": "1dd19f19e4b53a1fd2e8e738a08dd5fe635ec7e5", + "lastModified": 1755678602, + "narHash": "sha256-uEC5O/NIUNs1zmc1aH1+G3GRACbODjk2iS0ET5hXtuk=", + "owner": "hyprwm", + "repo": "hyprgraphics", + "rev": "157cc52065a104fc3b8fa542ae648b992421d1c7", "type": "github" }, "original": { - "owner": "nix-darwin", - "ref": "master", - "repo": "nix-darwin", + "owner": "hyprwm", + "repo": "hyprgraphics", + "type": "github" + } + }, + "hypridle": { + "inputs": { + "hyprland-protocols": "hyprland-protocols", + "hyprlang": "hyprlang", + "hyprutils": "hyprutils", + "hyprwayland-scanner": "hyprwayland-scanner", + "nixpkgs": "nixpkgs_2", + "systems": "systems" + }, + "locked": { + "lastModified": 1756291201, + "narHash": "sha256-YzRWE3rCnsY0WDRJcn4KvyWUoe+5zdkUYNIaHGP9BZ4=", + "owner": "hyprwm", + "repo": "hypridle", + "rev": "5430b73ddf148651bcf35fa39ed4d757c7534028", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hypridle", + "type": "github" + } + }, + "hyprland": { + "inputs": { + "aquamarine": "aquamarine", + "hyprcursor": "hyprcursor", + "hyprgraphics": "hyprgraphics", + "hyprland-protocols": "hyprland-protocols_2", + "hyprland-qtutils": "hyprland-qtutils", + "hyprlang": "hyprlang_2", + "hyprutils": "hyprutils_2", + "hyprwayland-scanner": "hyprwayland-scanner_2", + "nixpkgs": "nixpkgs_3", + "pre-commit-hooks": "pre-commit-hooks", + "systems": "systems_2", + "xdph": "xdph" + }, + "locked": { + "lastModified": 1756656879, + "narHash": "sha256-QCNUXw1J0BaykSKSb9jp5h1v4YVBLVcekhrxnivlgY4=", + "owner": "hyprwm", + "repo": "Hyprland", + "rev": "5bb8adbc3228901d199e8d22d6f712bd1d7d4e15", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "Hyprland", + "type": "github" + } + }, + "hyprland-protocols": { + "inputs": { + "nixpkgs": [ + "hydenix", + "hypridle", + "nixpkgs" + ], + "systems": [ + "hydenix", + "hypridle", + "systems" + ] + }, + "locked": { + "lastModified": 1749046714, + "narHash": "sha256-kymV5FMnddYGI+UjwIw8ceDjdeg7ToDVjbHCvUlhn14=", + "owner": "hyprwm", + "repo": "hyprland-protocols", + "rev": "613878cb6f459c5e323aaafe1e6f388ac8a36330", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprland-protocols", + "type": "github" + } + }, + "hyprland-protocols_2": { + "inputs": { + "nixpkgs": [ + "hydenix", + "hyprland", + "nixpkgs" + ], + "systems": [ + "hydenix", + "hyprland", + "systems" + ] + }, + "locked": { + "lastModified": 1749046714, + "narHash": "sha256-kymV5FMnddYGI+UjwIw8ceDjdeg7ToDVjbHCvUlhn14=", + "owner": "hyprwm", + "repo": "hyprland-protocols", + "rev": "613878cb6f459c5e323aaafe1e6f388ac8a36330", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprland-protocols", + "type": "github" + } + }, + "hyprland-qt-support": { + "inputs": { + "hyprlang": [ + "hydenix", + "hyprland", + "hyprland-qtutils", + "hyprlang" + ], + "nixpkgs": [ + "hydenix", + "hyprland", + "hyprland-qtutils", + "nixpkgs" + ], + "systems": [ + "hydenix", + "hyprland", + "hyprland-qtutils", + "systems" + ] + }, + "locked": { + "lastModified": 1749154592, + "narHash": "sha256-DO7z5CeT/ddSGDEnK9mAXm1qlGL47L3VAHLlLXoCjhE=", + "owner": "hyprwm", + "repo": "hyprland-qt-support", + "rev": "4c8053c3c888138a30c3a6c45c2e45f5484f2074", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprland-qt-support", + "type": "github" + } + }, + "hyprland-qtutils": { + "inputs": { + "hyprland-qt-support": "hyprland-qt-support", + "hyprlang": [ + "hydenix", + "hyprland", + "hyprlang" + ], + "hyprutils": [ + "hydenix", + "hyprland", + "hyprland-qtutils", + "hyprlang", + "hyprutils" + ], + "nixpkgs": [ + "hydenix", + "hyprland", + "nixpkgs" + ], + "systems": [ + "hydenix", + "hyprland", + "systems" + ] + }, + "locked": { + "lastModified": 1753819801, + "narHash": "sha256-tHe6XeNeVeKapkNM3tcjW4RuD+tB2iwwoogWJOtsqTI=", + "owner": "hyprwm", + "repo": "hyprland-qtutils", + "rev": "b308a818b9dcaa7ab8ccab891c1b84ebde2152bc", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprland-qtutils", + "type": "github" + } + }, + "hyprlang": { + "inputs": { + "hyprutils": [ + "hydenix", + "hypridle", + "hyprutils" + ], + "nixpkgs": [ + "hydenix", + "hypridle", + "nixpkgs" + ], + "systems": [ + "hydenix", + "hypridle", + "systems" + ] + }, + "locked": { + "lastModified": 1749145882, + "narHash": "sha256-qr0KXeczF8Sma3Ae7+dR2NHhvG7YeLBJv19W4oMu6ZE=", + "owner": "hyprwm", + "repo": "hyprlang", + "rev": "1bfb84f54d50c7ae6558c794d3cfd5f6a7e6e676", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprlang", + "type": "github" + } + }, + "hyprlang_2": { + "inputs": { + "hyprutils": [ + "hydenix", + "hyprland", + "hyprutils" + ], + "nixpkgs": [ + "hydenix", + "hyprland", + "nixpkgs" + ], + "systems": [ + "hydenix", + "hyprland", + "systems" + ] + }, + "locked": { + "lastModified": 1753622892, + "narHash": "sha256-0K+A+gmOI8IklSg5It1nyRNv0kCNL51duwnhUO/B8JA=", + "owner": "hyprwm", + "repo": "hyprlang", + "rev": "23f0debd2003f17bd65f851cd3f930cff8a8c809", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprlang", + "type": "github" + } + }, + "hyprutils": { + "inputs": { + "nixpkgs": [ + "hydenix", + "hypridle", + "nixpkgs" + ], + "systems": [ + "hydenix", + "hypridle", + "systems" + ] + }, + "locked": { + "lastModified": 1749135356, + "narHash": "sha256-Q8mAKMDsFbCEuq7zoSlcTuxgbIBVhfIYpX0RjE32PS0=", + "owner": "hyprwm", + "repo": "hyprutils", + "rev": "e36db00dfb3a3d3fdcc4069cb292ff60d2699ccb", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprutils", + "type": "github" + } + }, + "hyprutils_2": { + "inputs": { + "nixpkgs": [ + "hydenix", + "hyprland", + "nixpkgs" + ], + "systems": [ + "hydenix", + "hyprland", + "systems" + ] + }, + "locked": { + "lastModified": 1756117388, + "narHash": "sha256-oRDel6pNl/T2tI+nc/USU9ZP9w08dxtl7hiZxa0C/Wc=", + "owner": "hyprwm", + "repo": "hyprutils", + "rev": "b2ae3204845f5f2f79b4703b441252d8ad2ecfd0", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprutils", + "type": "github" + } + }, + "hyprwayland-scanner": { + "inputs": { + "nixpkgs": [ + "hydenix", + "hypridle", + "nixpkgs" + ], + "systems": [ + "hydenix", + "hypridle", + "systems" + ] + }, + "locked": { + "lastModified": 1749145760, + "narHash": "sha256-IHaGWpGrv7seFWdw/1A+wHtTsPlOGIKMrk1TUIYJEFI=", + "owner": "hyprwm", + "repo": "hyprwayland-scanner", + "rev": "817918315ea016cc2d94004bfb3223b5fd9dfcc6", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprwayland-scanner", + "type": "github" + } + }, + "hyprwayland-scanner_2": { + "inputs": { + "nixpkgs": [ + "hydenix", + "hyprland", + "nixpkgs" + ], + "systems": [ + "hydenix", + "hyprland", + "systems" + ] + }, + "locked": { + "lastModified": 1755184602, + "narHash": "sha256-RCBQN8xuADB0LEgaKbfRqwm6CdyopE1xIEhNc67FAbw=", + "owner": "hyprwm", + "repo": "hyprwayland-scanner", + "rev": "b3b0f1f40ae09d4447c20608e5a4faf8bf3c492d", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprwayland-scanner", + "type": "github" + } + }, + "ixx": { + "inputs": { + "flake-utils": [ + "nix-podman-stacks", + "search", + "flake-utils" + ], + "nixpkgs": [ + "nix-podman-stacks", + "search", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1754860581, + "narHash": "sha256-EM0IE63OHxXCOpDHXaTyHIOk2cNvMCGPqLt/IdtVxgk=", + "owner": "NuschtOS", + "repo": "ixx", + "rev": "babfe85a876162c4acc9ab6fb4483df88fa1f281", + "type": "github" + }, + "original": { + "owner": "NuschtOS", + "ref": "v0.1.1", + "repo": "ixx", + "type": "github" + } + }, + "jovian": { + "inputs": { + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "chaotic", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1756201372, + "narHash": "sha256-bK5j5cwJgO5AZXlDl5AgISzpOv9YV1Fcv2nDr9RW/5o=", + "owner": "Jovian-Experiments", + "repo": "Jovian-NixOS", + "rev": "9f6745bd704ab7f2617d41c2b02f4fd5f9ed0e89", + "type": "github" + }, + "original": { + "owner": "Jovian-Experiments", + "repo": "Jovian-NixOS", "type": "github" } }, "nix-gaming": { "inputs": { "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_4" }, "locked": { - "lastModified": 1746410227, - "narHash": "sha256-F2gKEIBfqfeQUcvMg0YD3xRnJIPyEgINR+ouTedoAtg=", + "lastModified": 1756604975, + "narHash": "sha256-qLvZwPsuUUL2B/yqFJBeWmz6fdsloQyMAWK/6dYDXMU=", "owner": "fufexan", "repo": "nix-gaming", - "rev": "3b68db5adeda4b4ac018aea0acf8ebb4941c4b15", + "rev": "4c92760b8d12d0d36e8e189d890d1c01a7ae646e", "type": "github" }, "original": { @@ -279,17 +781,38 @@ } }, "nix-index-database": { + "inputs": { + "nixpkgs": [ + "hydenix", + "hydenix-nixpkgs" + ] + }, + "locked": { + "lastModified": 1756008611, + "narHash": "sha256-rfTBWuTXi9/X7GhtF562FKNXKh2kvKb6dwI5lV1SjPE=", + "owner": "nix-community", + "repo": "nix-index-database", + "rev": "52dec1cb33a614accb9e01307e17816be974d24d", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-index-database", + "type": "github" + } + }, + "nix-index-database_2": { "inputs": { "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1746330942, - "narHash": "sha256-ShizFaJCAST23tSrHHtFFGF0fwd72AG+KhPZFFQX/0o=", + "lastModified": 1756612744, + "narHash": "sha256-/glV6VAq8Va3ghIbmhET3S1dzkbZqicsk5h+FtvwiPE=", "owner": "nix-community", "repo": "nix-index-database", - "rev": "137fd2bd726fff343874f85601b51769b48685cc", + "rev": "3fe768e1f058961095b4a0d7a2ba15dc9736bdc6", "type": "github" }, "original": { @@ -298,13 +821,36 @@ "type": "github" } }, + "nix-podman-stacks": { + "inputs": { + "home-manager": "home-manager_3", + "nixpkgs": [ + "nixpkgs" + ], + "search": "search", + "sops-nix": "sops-nix" + }, + "locked": { + "lastModified": 1756640992, + "narHash": "sha256-iNfoqZyShPzXcARe6lMXR9OM/b3uykgpEtyD983hH8Q=", + "owner": "Tarow", + "repo": "nix-podman-stacks", + "rev": "b84320f19bb906644be85aeab717dcf4870df2e6", + "type": "github" + }, + "original": { + "owner": "Tarow", + "repo": "nix-podman-stacks", + "type": "github" + } + }, "nixos-hardware": { "locked": { - "lastModified": 1742631601, - "narHash": "sha256-yJ3OOAmsGAxSl0bTmKUp3+cEYtSS+V6hUPK2rYhIPr8=", + "lastModified": 1756245047, + "narHash": "sha256-9bHzrVbjAudbO8q4vYFBWlEkDam31fsz0J7GB8k4AsI=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "380ed15bcd6440606c6856db44a99140d422b46f", + "rev": "a65b650d6981e23edd1afa1f01eb942f19cdcbb7", "type": "github" }, "original": { @@ -316,11 +862,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1737062831, - "narHash": "sha256-Tbk1MZbtV2s5aG+iM99U8FqwxU/YNArMcWAv6clcsBc=", + "lastModified": 1756542300, + "narHash": "sha256-tlOn88coG5fzdyqz6R93SQL5Gpq+m/DsWpekNFhqPQk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5df43628fdf08d642be8ba5b3625a6c70731c19c", + "rev": "d7600c775f877cd87b4f5a831c28aa94137377aa", "type": "github" }, "original": { @@ -332,11 +878,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1743296961, - "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "lastModified": 1753579242, + "narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e", "type": "github" }, "original": { @@ -347,43 +893,75 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1735651292, - "narHash": "sha256-YLbzcBtYo1/FEzFsB3AnM16qFc6fWPMIoOuSoDwvg9g=", + "lastModified": 1756650686, + "narHash": "sha256-kgf2GzjNcyZ9A68+NuRTiYOGgB3Ux8Bw4KU5WjjYk1A=", "owner": "nixos", "repo": "nixpkgs", - "rev": "0da3c44a9460a26d2025ec3ed2ec60a895eb1114", + "rev": "c8f89b1c7ce876a7d0a7f172f8fc78c63176ab2b", "type": "github" }, "original": { "owner": "nixos", - "ref": "release-24.05", + "ref": "release-25.05", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { "locked": { - "lastModified": 1746269363, - "narHash": "sha256-Q0lKWway9OmZnkDTpAoAE9VLXHOHqCqdJ3N0tkSM99g=", + "lastModified": 1748929857, + "narHash": "sha256-lcZQ8RhsmhsK8u7LIFsJhsLh/pzR9yZ8yqpTzyGdj+Q=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b6aef6c3553f849e1e6c08f1bcd3061df2b69fc4", + "rev": "c2a03962b8e24e669fb37b7df10e7c79531ff1a4", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-unstable", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_3": { "locked": { - "lastModified": 1743315132, - "narHash": "sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om+D4UnDhlDW9BE=", + "lastModified": 1756266583, + "narHash": "sha256-cr748nSmpfvnhqSXPiCfUPxRz2FJnvf/RjJGvFfaCsM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "8a6d5427d99ec71c64f0b93d45778c889005d9c2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1756536218, + "narHash": "sha256-ynQxPVN2FIPheUgTFhv01gYLbaiSOS7NgWJPm9LF9D0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a918bb3594dd243c2f8534b3be01b3cb4ed35fd1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_5": { + "locked": { + "lastModified": 1756542300, + "narHash": "sha256-tlOn88coG5fzdyqz6R93SQL5Gpq+m/DsWpekNFhqPQk=", "owner": "nixos", "repo": "nixpkgs", - "rev": "52faf482a3889b7619003c0daec593a1912fddc1", + "rev": "d7600c775f877cd87b4f5a831c28aa94137377aa", "type": "github" }, "original": { @@ -393,36 +971,137 @@ "type": "github" } }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "hydenix", + "hyprland", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1755960406, + "narHash": "sha256-RF7j6C1TmSTK9tYWO6CdEMtg6XZaUKcvZwOCD2SICZs=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "e891a93b193fcaf2fc8012d890dc7f0befe86ec2", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { "chaotic": "chaotic", - "deploy-rs": "deploy-rs", "hydenix": "hydenix", - "nix-darwin": "nix-darwin", "nix-gaming": "nix-gaming", - "nix-index-database": "nix-index-database", - "nixpkgs": "nixpkgs_3", + "nix-index-database": "nix-index-database_2", + "nix-podman-stacks": "nix-podman-stacks", + "nixpkgs": "nixpkgs_5", "nixpkgs-stable": "nixpkgs-stable" } }, - "rust-analyzer-src": { - "flake": false, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "chaotic", + "nixpkgs" + ] + }, "locked": { - "lastModified": 1736970696, - "narHash": "sha256-WP5yBCVkidEf5y3kCaUSjRd0udpAE6nmY3MMx3Q2aNo=", - "owner": "rust-lang", - "repo": "rust-analyzer", - "rev": "7d337c7f350a163ac3a9bd4ce0c4dae2df20579b", + "lastModified": 1756434910, + "narHash": "sha256-5UJRyxZ8QCm+pgh5pNHXFJMmopMqHVraUhRA1g2AmA0=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "86e5140961c91a9ee1dde1c17d18a787d44ceef8", "type": "github" }, "original": { - "owner": "rust-lang", - "ref": "nightly", - "repo": "rust-analyzer", + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "search": { + "inputs": { + "flake-utils": "flake-utils", + "ixx": "ixx", + "nixpkgs": [ + "nix-podman-stacks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1755555503, + "narHash": "sha256-WiOO7GUOsJ4/DoMy2IC5InnqRDSo2U11la48vCCIjjY=", + "owner": "NuschtOS", + "repo": "search", + "rev": "6f3efef888b92e6520f10eae15b86ff537e1d2ea", + "type": "github" + }, + "original": { + "owner": "NuschtOS", + "repo": "search", + "type": "github" + } + }, + "sops-nix": { + "inputs": { + "nixpkgs": [ + "nix-podman-stacks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1754988908, + "narHash": "sha256-t+voe2961vCgrzPFtZxha0/kmFSHFobzF00sT8p9h0U=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "3223c7a92724b5d804e9988c6b447a0d09017d48", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", "type": "github" } }, "systems": { + "locked": { + "lastModified": 1689347949, + "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", + "owner": "nix-systems", + "repo": "default-linux", + "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default-linux", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1689347949, + "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", + "owner": "nix-systems", + "repo": "default-linux", + "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default-linux", + "type": "github" + } + }, + "systems_3": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", @@ -437,21 +1116,50 @@ "type": "github" } }, - "utils": { + "xdph": { "inputs": { - "systems": "systems" + "hyprland-protocols": [ + "hydenix", + "hyprland", + "hyprland-protocols" + ], + "hyprlang": [ + "hydenix", + "hyprland", + "hyprlang" + ], + "hyprutils": [ + "hydenix", + "hyprland", + "hyprutils" + ], + "hyprwayland-scanner": [ + "hydenix", + "hyprland", + "hyprwayland-scanner" + ], + "nixpkgs": [ + "hydenix", + "hyprland", + "nixpkgs" + ], + "systems": [ + "hydenix", + "hyprland", + "systems" + ] }, "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "lastModified": 1755354946, + "narHash": "sha256-zdov5f/GcoLQc9qYIS1dUTqtJMeDqmBmo59PAxze6e4=", + "owner": "hyprwm", + "repo": "xdg-desktop-portal-hyprland", + "rev": "a10726d6a8d0ef1a0c645378f983b6278c42eaa0", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "hyprwm", + "repo": "xdg-desktop-portal-hyprland", "type": "github" } } diff --git a/flake.nix b/flake.nix index 5043797..e5ed61c 100755 --- a/flake.nix +++ b/flake.nix @@ -6,13 +6,11 @@ # User's nixpkgs - for user packages nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - nixpkgs-stable.url = "github:nixos/nixpkgs/release-24.05"; + nixpkgs-stable.url = "github:nixos/nixpkgs/release-25.05"; nix-gaming.url = "github:fufexan/nix-gaming"; # Hydenix and its nixpkgs - kept separate to avoid conflicts - hydenix = { - url = "github:richen604/hydenix"; - }; + hydenix.url = "github:richen604/hydenix/v4.9.0"; # Nix-index-database - for comma and command-not-found nix-index-database = { @@ -20,18 +18,13 @@ inputs.nixpkgs.follows = "nixpkgs"; }; - deploy-rs = { - url = "github:serokell/deploy-rs"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - - nix-darwin = { - url = "github:nix-darwin/nix-darwin/master"; + nix-podman-stacks = { + url = "github:Tarow/nix-podman-stacks"; inputs.nixpkgs.follows = "nixpkgs"; }; }; - outputs = { self, nix-darwin, ... }@inputs: + outputs = { self, ... }@inputs: let vars = { user = "mirage"; @@ -51,20 +44,6 @@ ]; }; - # Create macOS configuration - mkDarwinHost = - hostname: - nix-darwin.lib.darwinSystem { - system = "aarch64-darwin"; # or "x86_64-darwin" for Intel Macs - specialArgs = { - inherit inputs vars; - hostname = hostname; - }; - modules = [ - ./hosts/darwin/${hostname} - ]; - }; - # Create VM variant function mkVm = hostname: @@ -78,87 +57,174 @@ flake = inputs.self.outPath; }; - # All below is for deploy-rs system = inputs.hydenix.lib.system; - pkgs = import inputs.nixpkgs { - inherit system; - overlays = [ inputs.deploy-rs.overlays.default ]; + pkgs = import inputs.nixpkgs { inherit system; }; + + # Host configuration mapping + hostConfigs = { + fern = { hostname = "fern"; ip = "fern"; }; + oak = { hostname = "oak"; ip = "oak"; }; + pine = { hostname = "pine"; ip = "pine"; }; + cedar = { hostname = "cedar"; ip = "cedar"; }; }; - mkDeployNode = hostname: { - hostname = hostname; - profiles.system = { - user = "${vars.user}"; - path = inputs.deploy-rs.lib.${system}.activate.nixos inputs.self.nixosConfigurations.${hostname}; - sshUser = "${vars.user}"; - interactiveSudo = true; - sshOpts = [ - "-p" - "22" - ]; - magicRollback = true; - confirmTimeout = 300; - }; - }; in { nixosConfigurations = { fern = mkHost "fern"; oak = mkHost "oak"; pine = mkHost "pine"; - "fern.local" = mkHost "fern"; - "oak.local" = mkHost "oak"; - "pine.local" = mkHost "pine"; - }; - - darwinConfigurations = { - # Replace "macbook" with your actual hostname (run 'scutil --get LocalHostName' to find it) - macbook = mkDarwinHost "macbook"; - }; - - deploy.nodes = { - fern = mkDeployNode "fern.local"; - oak = mkDeployNode "oak.local"; - pine = mkDeployNode "pine.local"; + cedar = mkHost "cedar"; }; - packages.${inputs.hydenix.lib.system} = { + packages.${system} = { + cedar-vm = mkVm "cedar"; fern-vm = mkVm "fern"; oak-vm = mkVm "oak"; - build-iso = isoConfig.build-iso; - burn-iso = isoConfig.burn-iso; + fern = self.nixosConfigurations.fern.config.system.build.toplevel; + oak = self.nixosConfigurations.oak.config.system.build.toplevel; + cedar = self.nixosConfigurations.cedar.config.system.build.toplevel; + # Replacement for deploy-rs: nixos-rebuild based deployment script rb = pkgs.writeShellScriptBin "rb" '' set -euo pipefail + host=$1 + shift # Remove the first argument (hostname) + + # Parse additional flags + build_host="" + extra_args=() + + while [[ $# -gt 0 ]]; do + case "$1" in + --build-host=*) + build_host="''${1#*=}" + shift + ;; + --build-host) + build_host="$2" + shift 2 + ;; + --remote-build) + build_host="''${hostConfigs[$host]:-$host}" + shift + ;; + --local-build) + build_host="localhost" + shift + ;; + *) + extra_args+=("$1") + shift + ;; + esac + done + + # Get host configuration case "$host" in - "oak") - ${pkgs.deploy-rs}/bin/deploy --skip-checks .#oak ;; - "fern") - ${pkgs.deploy-rs}/bin/deploy --skip-checks .#fern ;; - "pine") - ${pkgs.deploy-rs}/bin/deploy --skip-checks .#pine ;; - "all") - ${pkgs.deploy-rs}/bin/deploy --skip-checks .#oak - ${pkgs.deploy-rs}/bin/deploy --skip-checks .#fern - ${pkgs.deploy-rs}/bin/deploy --skip-checks .#pine - ;; - *) echo "Usage: rb [oak|fern|pine|all]" >&2; exit 1 ;; + "oak"|"fern"|"pine"|"cedar") + target_host="''${hostConfigs[$host]}" + ;; + "all") + # Deploy to all hosts sequentially + echo "Deploying to all hosts..." + for h in oak fern pine cedar; do + echo "=== Deploying to $h ===" + ${pkgs.nix}/bin/nixos-rebuild switch \ + --flake ".#$h" \ + --target-host "$h" \ + --sudo \ + "''${extra_args[@]}" + done + exit 0 + ;; + *) + echo "Usage: rb [oak|fern|pine|cedar|all] [OPTIONS] [-- nixos-rebuild-args]" + echo "" + echo "Options:" + echo " --build-host=HOST Build on specified host" + echo " --remote-build Build on target host (default)" + echo " --local-build Build on local machine" + echo " -- nixos-rebuild-args Additional arguments to nixos-rebuild" + echo "" + echo "Examples:" + echo " rb cedar # Build on cedar, deploy to cedar" + echo " rb cedar --local-build # Build locally, deploy to cedar" + echo " rb cedar --build-host=fern # Build on fern, deploy to cedar" + echo " rb cedar -- --show-trace # Pass --show-trace to nixos-rebuild" + exit 1 + ;; esac + + # Build the nixos-rebuild command + cmd=( + ${pkgs.nix}/bin/nixos-rebuild + switch + --flake ".#$host" + --target-host "$target_host" + --sudo + ) + + # Add build-host if specified + if [[ -n "$build_host" ]]; then + cmd+=(--build-host "$build_host") + fi + + # Add extra arguments + cmd+=("''${extra_args[@]}") + + echo "Running: ''${cmd[@]}" + "''${cmd[@]}" + ''; + + # Helper scripts for common deployment patterns + deploy-remote = pkgs.writeShellScriptBin "deploy-remote" '' + # Build on target host (remote build) + ${self.packages.${system}.rb}/bin/rb "$1" --remote-build "''${@:2}" + ''; + + deploy-local = pkgs.writeShellScriptBin "deploy-local" '' + # Build on local machine + ${self.packages.${system}.rb}/bin/rb "$1" --local-build "''${@:2}" + ''; + + deploy-cross = pkgs.writeShellScriptBin "deploy-cross" '' + # Build on different host than target + if [ $# -lt 3 ]; then + echo "Usage: deploy-cross TARGET_HOST BUILD_HOST [EXTRA_ARGS]" + exit 1 + fi + target_host=$1 + build_host=$2 + ${self.packages.${system}.rb}/bin/rb "$target_host" --build-host="$build_host" "''${@:3}" ''; }; - checks.${system} = { - oak-check = inputs.deploy-rs.lib.${system}.deployChecks { - nodes.oak = inputs.self.deploy.nodes.oak; - }; - fern-check = inputs.deploy-rs.lib.${system}.deployChecks { - nodes.fern = inputs.self.deploy.nodes.fern; - }; - pine-check = inputs.deploy-rs.lib.${system}.deployChecks { - nodes.pine = inputs.self.deploy.nodes.pine; - }; + # Development shell with deployment tools + devShells.${system}.default = pkgs.mkShell { + buildInputs = [ + self.packages.${system}.rb + self.packages.${system}.deploy-remote + self.packages.${system}.deploy-local + self.packages.${system}.deploy-cross + pkgs.nix + ]; + + shellHook = '' + echo "Available deployment commands:" + echo " rb [host] [options] - Flexible deployment (replacement for deploy-rs)" + echo " deploy-remote [host] - Build on target host" + echo " deploy-local [host] - Build on local machine" + echo " deploy-cross [target] [build] - Cross-host deployment" + echo "" + echo "Examples:" + echo " rb cedar --local-build -- --show-trace" + echo " deploy-remote oak" + echo " deploy-local fern" + echo " deploy-cross pine oak" + ''; }; }; } \ No newline at end of file diff --git a/hosts/cedar/default.nix b/hosts/cedar/default.nix new file mode 100644 index 0000000..4071596 --- /dev/null +++ b/hosts/cedar/default.nix @@ -0,0 +1,130 @@ +{ + inputs, + vars, + lib, + ... +}: +let + pkgs = import inputs.hydenix.inputs.hydenix-nixpkgs { + inherit (inputs.hydenix.lib) system; + config = { + allowUnfree = true; + }; + overlays = [ + inputs.hydenix.lib.overlays + (final: prev: { + userPkgs = import inputs.nixpkgs { + inherit (inputs.hydenix.lib) system; + config = { + allowUnfree = true; + }; + }; + }) + ]; + }; +in +{ + nixpkgs.pkgs = pkgs; + + imports = [ + inputs.hydenix.inputs.home-manager.nixosModules.home-manager + inputs.hydenix.lib.nixOsModules + ./hardware-configuration.nix + ../../modules/system/hosts/cedar + + + # === GPU-specific configurations === + + /* + For drivers, we are leveraging nixos-hardware + Most common drivers are below, but you can see more options here: https://github.com/NixOS/nixos-hardware + */ + + #! EDIT THIS SECTION + # === Other common modules === + ]; + + home-manager = { + useGlobalPkgs = true; + useUserPackages = true; + extraSpecialArgs = { + inherit inputs; + }; + users."${vars.user}" = + { ... }: + { + # hm import + imports = [ + ../../modules/hm/desktops/hydenix.nix + ../../modules/hm/hosts/cedar + ]; + + desktops.hydenix = { + enable = false; + hostname = "cedar"; + }; + }; + }; + + hydenix = { + enable = true; + hostname = "cedar"; + timezone = "Europe/Paris"; + locale = "fr_FR.UTF-8"; + + audio.enable = false; + sddm.enable = false; + boot.useSystemdBoot = false; + hardware.enable = false; + system.enable = false; + gaming.enable = false; + }; + + boot.loader = { + grub = { + device = lib.mkForce "/dev/sda"; + efiSupport = lib.mkForce false; + }; + efi.canTouchEfiVariables = lib.mkForce false; + }; + + # Select internationalisation properties. + i18n.extraLocaleSettings = { + LC_ADDRESS = "fr_FR.UTF-8"; + LC_IDENTIFICATION = "fr_FR.UTF-8"; + LC_MEASUREMENT = "fr_FR.UTF-8"; + LC_MONETARY = "fr_FR.UTF-8"; + LC_NAME = "fr_FR.UTF-8"; + LC_NUMERIC = "fr_FR.UTF-8"; + LC_PAPER = "fr_FR.UTF-8"; + LC_TELEPHONE = "fr_FR.UTF-8"; + LC_TIME = "fr_FR.UTF-8"; + }; + # Configure keymap in X11 + services.xserver.xkb = { + layout = "fr"; + variant = ""; + }; + # Configure console keymap + console.keyMap = "fr"; + # Define a user account. Don't forget to set a password with ‘passwd’. + users.users.${vars.user} = { + isNormalUser = true; + description = "${vars.user}"; + # initialPassword = "${vars.user}"; # Uncomment to set a password on first boot. + extraGroups = [ "networkmanager" "wheel" "nix-ssh" ]; + shell = pkgs.zsh; + }; + + programs.zsh.enable = true; + # Enable automatic login for the user. + services.getty.autologinUser = "${vars.user}"; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It‘s perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "25.05"; # Did you read the comment? +} diff --git a/hosts/cedar/hardware-configuration.nix b/hosts/cedar/hardware-configuration.nix new file mode 100755 index 0000000..2f92c6e --- /dev/null +++ b/hosts/cedar/hardware-configuration.nix @@ -0,0 +1,31 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/profiles/qemu-guest.nix") + ]; + + boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/e2370871-8442-4f09-885e-93fed689ac8f"; + fsType = "ext4"; + }; + + swapDevices = [ ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.ens18.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} \ No newline at end of file diff --git a/hosts/oak/default.nix b/hosts/oak/default.nix index b9e49d0..0e77887 100644 --- a/hosts/oak/default.nix +++ b/hosts/oak/default.nix @@ -1,6 +1,7 @@ { inputs, vars, + lib, ... }: let @@ -9,6 +10,9 @@ let config = { android_sdk.accept_license = true; allowUnfree = true; + permittedInsecurePackages = [ + "qtwebengine-5.15.19" + ]; }; overlays = [ inputs.hydenix.lib.overlays @@ -18,6 +22,9 @@ let config = { android_sdk.accept_license = true; allowUnfree = true; + permittedInsecurePackages = [ + "qtwebengine-5.15.19" + ]; }; }; }) @@ -48,7 +55,7 @@ in inputs.hydenix.inputs.nixos-hardware.nixosModules.asus-fa507nv ]; - boot.kernelParams = [ "video=HDMI-A-1:e" ]; + #boot.kernelParams = [ "video=HDMI-A-1:e" ]; home-manager = { useGlobalPkgs = true; @@ -77,6 +84,7 @@ in hostname = "oak"; timezone = "Europe/Paris"; locale = "fr_FR.UTF-8"; + gaming.enable = false; }; users.users.${vars.user} = { @@ -91,4 +99,10 @@ in ]; shell = pkgs.zsh; }; + + hardware.nvidia = { + prime.amdgpuBusId = lib.mkForce "PCI:36:0:0"; + }; + + #boot.kernel.sysctl."net.ipv4.ip_unprivileged_port_start" = 0; } diff --git a/hosts/oak/hardware-configuration.nix b/hosts/oak/hardware-configuration.nix index 33cb0f5..e14fba8 100644 --- a/hosts/oak/hardware-configuration.nix +++ b/hosts/oak/hardware-configuration.nix @@ -1,7 +1,7 @@ # Do not modify this file! It was generated by ‘nixos-generate-config’ # and may be overwritten by future invocations. Please make changes # to /etc/nixos/configuration.nix instead. -{ config, lib, pkgs, modulesPath, ... }: +{ config, lib, pkgs, modulesPath, vars, ... }: { imports = @@ -24,6 +24,12 @@ options = [ "fmask=0077" "dmask=0077" ]; }; + fileSystems."/home/${vars.user}/mnt" = { + device = "/dev/disk/by-uuid/cd70c50f-c6c2-49c1-935a-854ba3e8c153"; + fsType = "ext4"; + options = [ "defaults" "noatime" "discard" "nofail" "errors=remount-ro" ]; + }; + swapDevices = [ ]; # Enables DHCP on each ethernet and wireless interface. In case of scripted networking diff --git a/hosts/pine/default.nix b/hosts/pine/default.nix index 1cdf2ab..8f9633d 100644 --- a/hosts/pine/default.nix +++ b/hosts/pine/default.nix @@ -14,7 +14,9 @@ let (final: prev: { userPkgs = import inputs.nixpkgs { inherit (inputs.hydenix.lib) system; - config.allowUnfree = true; + config = { + allowUnfree = true; + }; }; }) ]; @@ -27,7 +29,7 @@ in inputs.hydenix.inputs.home-manager.nixosModules.home-manager inputs.hydenix.lib.nixOsModules ./hardware-configuration.nix - ../../modules/system/hosts/pine + ../../modules/system/hosts/cedar # === GPU-specific configurations === @@ -39,12 +41,8 @@ in #! EDIT THIS SECTION # === Other common modules === - inputs.hydenix.inputs.nixos-hardware.nixosModules.common-pc - inputs.hydenix.inputs.nixos-hardware.nixosModules.common-pc-ssd ]; - boot.kernelParams = [ "video=HDMI-A-1:e" ]; - home-manager = { useGlobalPkgs = true; useUserPackages = true; @@ -56,19 +54,20 @@ in { # hm import imports = [ - ../../modules/hm/hosts/pine + ../../modules/hm/desktops/hydenix.nix + ../../modules/hm/hosts/cedar.nix ]; desktops.hydenix = { enable = true; - hostname = "pine"; + hostname = "cedar"; }; }; }; hydenix = { enable = true; - hostname = "pine"; + hostname = "cedar"; timezone = "Europe/Paris"; locale = "fr_FR.UTF-8"; }; diff --git a/hosts/pine/hardware-configuration.nix b/hosts/pine/hardware-configuration.nix new file mode 100755 index 0000000..f5aba8e --- /dev/null +++ b/hosts/pine/hardware-configuration.nix @@ -0,0 +1,29 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, pkgs, modulesPath, ... }: +{ + imports = + [ (modulesPath + "/profiles/qemu-guest.nix") + ]; + + boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ahci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + fileSystems."/" = + { device = "/dev/disk/by-uuid/a159c717-cc97-454e-b03a-6ca78857d646"; + fsType = "ext4"; + }; + swapDevices = + [ { device = "/dev/disk/by-uuid/8ec7d09a-f26a-4a2d-964d-f33bf8637055"; } + ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.ens18.useDHCP = lib.mkDefault true; + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} \ No newline at end of file diff --git a/info.txt b/info.txt index 487f47b..262e2f0 100644 --- a/info.txt +++ b/info.txt @@ -1,16 +1,3 @@ - home.file = { - ".config/hypr/userprefs.conf" = lib.mkForce { - text = '' - # Set keyboard layout to French - input { - kb_layout = fr - } - ''; - force = true; - mutable = true; - }; - }; - /* ! Below are defaults @@ -107,4 +94,19 @@ waybar.enable = true; # enable waybar module wlogout.enable = true; # enable wlogout module xdg.enable = true; # enable xdg module - */ \ No newline at end of file + */ + + + tarow.podman = { + hostIP4Address = "127.0.0.1"; + # This is the UID of the user that will own the podman containers + # and volumes created by tarow.podman. + # It should match the UID of the user running the tarow.podman service. + # If you are not sure, you can find it by running `id -u` in the terminal. + hostUid = 1000; + externalStorageBaseDir = "${config.home.homeDirectory}/podman-stoorage"; + + stacks = { + homepage.enable = true; + }; + }; \ No newline at end of file diff --git a/modules/hm/common/communication/mail/default.nix b/modules/hm/common/communication/mail.nix similarity index 64% rename from modules/hm/common/communication/mail/default.nix rename to modules/hm/common/communication/mail.nix index 876b42d..d83aa54 100644 --- a/modules/hm/common/communication/mail/default.nix +++ b/modules/hm/common/communication/mail.nix @@ -7,8 +7,8 @@ let # Map service names to their corresponding packages or list of packages serviceToPackage = { - thunderbird = [ pkgs.thunderbird-latest ]; - bluemail = [ (import ./bluemail.nix { inherit pkgs lib config; }).bluemailWithGPU ]; + thunderbird = [ ]; + bluemail = [ pkgs.bluemail ]; }; # Flatten the list of packages from all enabled services @@ -25,5 +25,18 @@ in config = { home.packages = packagesToInstall; + + programs.thunderbird = { + enable = lib.elem "thunderbird" serviceList; + profiles = { + default = { + isDefault = true; + settings = { + "mail.spellcheck.inline" = true; + "browser.display.use_system_colors" = true; + }; + }; + }; + }; }; } \ No newline at end of file diff --git a/modules/hm/common/communication/mail/bluemail.nix b/modules/hm/common/communication/mail/bluemail.nix deleted file mode 100644 index c3c11dc..0000000 --- a/modules/hm/common/communication/mail/bluemail.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ pkgs, config, lib, ... }: -let - bluemail = pkgs.bluemail; - - bluemailWithGPU = pkgs.symlinkJoin { - name = "bluemail-with-gpu"; - paths = [ bluemail ]; - nativeBuildInputs = [ pkgs.makeWrapper ]; - postBuild = '' - rm -f $out/bin/bluemail - makeWrapper ${bluemail}/bin/bluemail $out/bin/bluemail --add-flags "--in-process-gpu" - ''; - }; - -in { - # export for other module - bluemailWithGPU = bluemailWithGPU; -} \ No newline at end of file diff --git a/modules/hm/common/communication/matrix.nix b/modules/hm/common/communication/matrix.nix new file mode 100644 index 0000000..9798642 --- /dev/null +++ b/modules/hm/common/communication/matrix.nix @@ -0,0 +1,32 @@ +{ pkgs, lib, config, ... }: + +let + cfg = config.modules.hm.communication.matrix; + + # Map clients to their packages + clientsToPackage = with pkgs; { + element = [ ]; + nheko = [ ]; + }; + + # Get packages for enabled clients + clientPackages = lib.concatMap (client: clientsToPackage.${client} or []) cfg.clients; + +in { + options.modules.hm.communication.matrix = { + clients = lib.mkOption { + type = lib.types.listOf (lib.types.enum (lib.attrNames clientsToPackage)); + default = []; + description = "List of clients to install"; + }; + }; + + config = { + home.packages = clientPackages; + + programs = { + element-desktop.enable = lib.elem "element" cfg.clients; + nheko.enable = lib.elem "nheko" cfg.clients; + }; + }; +} \ No newline at end of file diff --git a/modules/hm/common/default.nix b/modules/hm/common/default.nix index 285630d..7419427 100644 --- a/modules/hm/common/default.nix +++ b/modules/hm/common/default.nix @@ -1,60 +1,91 @@ -{ - ... -}: +{ inputs, lib, config, ... }: +let + cfg = config.modules.hm.nh; +in { imports = [ ./browser - ./communication/mail + ./communication/mail.nix ./communication/discord.nix + ./communication/matrix.nix ./communication/teams.nix - ./dev/editor/dbeaver.nix - ./dev/editor/jetbrains.nix - ./dev/editor/vs-code.nix - ./dev/editor/android-studio.nix - ./dev/global-tools/act-github.nix - ./dev/global-tools/nix.nix - ./dev/global-tools/cli.nix - ./dev/node/node.nix - ./dev/node/pm2.nix - ./dev/node/prisma.nix - ./dev/dotnet.nix - ./dev/python.nix - ./dev/rust.nix + ./dev/environments/containers.nix + ./dev/environments/editor.nix + ./dev/languages/dotnet.nix + ./dev/languages/node.nix + ./dev/languages/python.nix + ./dev/languages/rust.nix + ./dev/tools/cli.nix + ./dev/tools/git-action.nix + ./dev/tools/gitleaks.nix + ./dev/tools/nix.nix + ./dev/tools/prisma.nix ./documentation/obsidian.nix ./documentation - ./emulator - ./engine - ./extra/ignore-file-retriever.nix + ./extra/shader-cache-cleanup.nix + ./extra/syncthing-ignore.nix ./games/games.nix ./games/joystick.nix ./games/mangohud.nix - ./multimedia/gimp.nix - ./multimedia/mpv.nix - ./multimedia/obs.nix - ./multimedia/openshot-qt.nix - ./multimedia/parsec.nix - ./multimedia/stremio.nix + ./multimedia/editing/image.nix + ./multimedia/editing/video.nix + ./multimedia/player.nix + ./multimedia/rambox.nix + ./multimedia/remote-desktop.nix + ./multimedia/streaming.nix ./network/tunnel.nix - ./shell/btop.nix - ./shell/fzf.nix - ./shell/navi.nix - ./shell/ranger.nix - ./shell/starship.nix + ./shell/disk-usage.nix ./shell/tools.nix + ./utilities/safety/ianny.nix + ./utilities/app-launcher.nix ./utilities/filezilla.nix ./utilities/kde-connect.nix ./utilities/scalar.nix ./utilities/stacer.nix + + inputs.nix-podman-stacks.homeModules.nps ]; -} + + options.modules.hm.nh = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Enable Home Manager configuration for this host"; + }; + clean.extraArgs = lib.mkOption { + type = lib.types.str; + default = "--keep-since 4d --keep 3"; + description = "Extra arguments for NH clean up"; + }; + flakePath = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Absolute path to the flake, or null"; + }; + }; + + config = { + programs = { + home-manager.enable = true; + nh = { + enable = cfg.enable; + clean = { + enable = cfg.enable; + extraArgs = cfg.clean.extraArgs; + }; + flake = cfg.flakePath; + }; + }; + }; +} \ No newline at end of file diff --git a/modules/hm/common/dev/editor/android-studio.nix b/modules/hm/common/dev/editor/android-studio.nix deleted file mode 100644 index 84af0ba..0000000 --- a/modules/hm/common/dev/editor/android-studio.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.dev.editor.android-studio; -in -{ - options.modules.hm.dev.editor.android-studio = { - enable = lib.mkEnableOption "Enable Android Studio development environment"; - }; - - config = lib.mkIf cfg.enable { - home.packages = (with pkgs; [ - android-studio - android-studio-tools - ]); - }; -} diff --git a/modules/hm/common/dev/editor/dbeaver.nix b/modules/hm/common/dev/editor/dbeaver.nix deleted file mode 100644 index ba96f26..0000000 --- a/modules/hm/common/dev/editor/dbeaver.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.dev.editor.dbeaver; -in -{ - options.modules.hm.dev.editor.dbeaver = { - enable = lib.mkEnableOption "Enable DBeaver database management tool"; - }; - - config = lib.mkIf cfg.enable { - home.packages = (with pkgs; [ - dbeaver-bin - ]); - }; -} diff --git a/modules/hm/common/dev/editor/jetbrains.nix b/modules/hm/common/dev/editor/jetbrains.nix deleted file mode 100644 index cee4d15..0000000 --- a/modules/hm/common/dev/editor/jetbrains.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.dev.editor.jetbrains; -in -{ - options.modules.hm.dev.editor.jetbrains = { - enable = lib.mkEnableOption "Enable JetBrains IDEs for development"; - - ides = lib.mkOption { - type = lib.types.listOf (lib.types.enum [ "webstorm" "rider" "phpstorm" "datagrip" ]); - default = ["webstorm" "rider" "phpstorm" "datagrip"]; - description = "List of JetBrains IDEs to install (e.g., webstorm, rider, phpstorm, datagrip)"; - }; - }; - - config = lib.mkIf cfg.enable { - home.packages = lib.optionals (cfg.ides != []) - (lib.concatMap (ide: - let - idePackage = builtins.getAttr ide pkgs.jetbrains; - in - [ idePackage ] - ) (lib.unique cfg.ides)); - }; -} diff --git a/modules/hm/common/dev/editor/vs-code.nix b/modules/hm/common/dev/editor/vs-code.nix deleted file mode 100644 index 5f2c2e0..0000000 --- a/modules/hm/common/dev/editor/vs-code.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ config, lib, pkgs, vars, ... }: - -let - cfg = config.modules.hm.dev.editor.vs-code; -in -{ - # Add options for vs-code - options.modules.hm.dev.editor.vs-code = { - enable = lib.mkEnableOption "Enable Visual Studio Code for development"; - }; - - # VS Code configuration, conditional on vs-code.enable - config = lib.mkIf cfg.enable { - programs.vscode = { - enable = true; - }; - }; -} \ No newline at end of file diff --git a/modules/hm/common/dev/environments/containers.nix b/modules/hm/common/dev/environments/containers.nix new file mode 100644 index 0000000..a79f96a --- /dev/null +++ b/modules/hm/common/dev/environments/containers.nix @@ -0,0 +1,116 @@ +{ pkgs, lib, config, ... }: + +let + cfg = config.modules.hm.dev.environments.containers; + + enginePackages = engine: + if engine == "docker" then [ pkgs.docker-compose ] + else if engine == "podman" then [ pkgs.podman-compose ] + else []; + + hasDocker = lib.lists.elem "docker" cfg.engine; + hasPodman = lib.lists.elem "podman" cfg.engine; + onlyOneEngine = lib.length cfg.engine == 1; +in +{ + options.modules.hm.dev.environments.containers = { + engine = lib.mkOption { + type = lib.types.listOf (lib.types.enum ["docker" "podman"]); + default = ["docker"]; + description = "List of container engines to use (docker and/or podman)"; + }; + gui.enable = lib.mkEnableOption "Enable GUI for container management"; + tui.enable = lib.mkEnableOption "Enable TUI for container management"; + helper.enable = lib.mkEnableOption "Enable helper tools for container management"; + overrideAliases = lib.mkEnableOption "Override all aliases for container management tools"; + enableSocket = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Enable socket activation for the container engine"; + }; + hostUid = lib.mkOption { + type = lib.types.int; + default = 1000; + description = "User ID for the podman socket location"; + }; + }; + + config = lib.mkMerge [ + { + services.podman = lib.mkIf hasPodman { + enable = true; + #settings.containers.network.dns_bind_port = 1153; + }; + + systemd.user.sockets.podman = lib.mkIf (hasPodman && cfg.enableSocket) { + Install.WantedBy = ["sockets.target"]; + Socket = { + SocketMode = "0660"; + ListenStream = "/run/user/${toString cfg.hostUid}/podman/podman.sock"; + }; + }; + + systemd.user.services.podman = lib.mkIf (hasPodman && cfg.enableSocket) { + Install.WantedBy = ["default.target"]; + Service = { + Delegate = true; + Type = "exec"; + KillMode = "process"; + Environment = ["LOGGING=--log-level=info"]; + ExecStart = "${lib.getExe pkgs.podman} $LOGGING system service"; + }; + }; + } + + { + home.packages = with pkgs; + (lib.concatMap enginePackages cfg.engine) + ++ lib.optionals cfg.gui.enable [ podman-desktop ] + ++ lib.optionals cfg.tui.enable ( + lib.optional hasDocker dry ++ lib.optional hasPodman podman-tui + ) + ++ lib.optionals cfg.helper.enable [ lazydocker ]; + } + + { + home.shellAliases = lib.mkIf cfg.overrideAliases ( + if onlyOneEngine then + if hasPodman then { + docker = "podman"; + docker-compose = "podman-compose"; + docker-tui = "podman-tui"; + } else { + podman = "docker"; + podman-compose = "docker-compose"; + docker-tui = "dry"; + docker-help = "lazydocker"; + } + else + {} + ); + } + + { + assertions = [ + { + assertion = !(cfg.overrideAliases && lib.length cfg.engine > 1); + message = '' + Cannot safely override aliases with multiple container engines enabled. + Either: + 1. Set modules.hm.dev.containers.overrideAliases = false + 2. Select only one engine in modules.hm.dev.containers.engine + ''; + } + { + assertion = !(cfg.helper.enable && hasPodman); + message = '' + Podman does not require lazydocker as it has its own TUI. + So: + 1. Set modules.hm.dev.containers.helper.enable = false + 2. Use podman-tui instead + ''; + } + ]; + } + ]; +} \ No newline at end of file diff --git a/modules/hm/common/dev/environments/editor.nix b/modules/hm/common/dev/environments/editor.nix new file mode 100644 index 0000000..ca39d9e --- /dev/null +++ b/modules/hm/common/dev/environments/editor.nix @@ -0,0 +1,67 @@ +{ pkgs, lib, config, ... }: + +let + cfg = config.modules.hm.dev.environments; + + # Categorize different types of IDEs + jetbrainsIDEs = with pkgs; { + datagrip = jetbrains.datagrip; + dataspell = jetbrains.dataspell; + fleet = jetbrains.fleet; + gateway = jetbrains.gateway; + phpstorm = jetbrains.phpstorm; + rider = jetbrains.rider; + webstorm = jetbrains.webstorm; + }; + + otherIDEs = with pkgs; { + android-studio = [ android-studio android-studio-tools ]; + dbeaver = dbeaver-bin; + vs-code = null; + }; + + # Combine all IDE options + allIDEs = jetbrainsIDEs // otherIDEs; + + # Get packages for enabled IDEs (excluding vs-code which is handled separately) + idePackages = lib.concatMap (ide: + let pkg = allIDEs.${ide}; + in + if pkg == null then [] + else if lib.isList pkg then pkg + else [ pkg ] + ) cfg.ides; + + # Get just the JetBrains packages for remote support + jetbrainsPackages = lib.concatMap (ide: + if lib.hasAttr ide jetbrainsIDEs then [ jetbrainsIDEs.${ide} ] else [] + ) cfg.ides; + + hasRemoteSupport = jetbrainsPackages != []; +in +{ + options.modules.hm.dev.environments = { + ides = lib.mkOption { + type = lib.types.listOf (lib.types.enum (lib.attrNames allIDEs)); + default = []; + example = [ "webstorm" "android-studio" "datagrip" "vs-code" ]; + description = "List of IDEs to install"; + }; + }; + + config = { + home.packages = idePackages; + + programs = { + jetbrains-remote = lib.mkIf hasRemoteSupport { + enable = true; + ides = jetbrainsPackages; + }; + + vscode = { + enable = lib.elem "vs-code" cfg.ides; + package = pkgs.vscode; + }; + }; + }; +} \ No newline at end of file diff --git a/modules/hm/common/dev/global-tools/act-github.nix b/modules/hm/common/dev/global-tools/act-github.nix deleted file mode 100644 index 61f8c56..0000000 --- a/modules/hm/common/dev/global-tools/act-github.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.dev.global-tools.act-github; -in -{ - options.modules.hm.dev.global-tools.act-github = { - enable = lib.mkEnableOption "Enable act for running GitHub Actions locally"; - }; - - config = lib.mkIf cfg.enable { - home.packages = (with pkgs; [ - act - ]); - }; -} diff --git a/modules/hm/common/dev/dotnet.nix b/modules/hm/common/dev/languages/dotnet.nix similarity index 90% rename from modules/hm/common/dev/dotnet.nix rename to modules/hm/common/dev/languages/dotnet.nix index 3a2d92f..bd7ba90 100644 --- a/modules/hm/common/dev/dotnet.nix +++ b/modules/hm/common/dev/languages/dotnet.nix @@ -1,7 +1,7 @@ { pkgs, lib, config, ... }: let - cfg = config.modules.hm.dev.dotnet; + cfg = config.modules.hm.dev.languages.dotnet; sdkVersions = cfg.sdk-versions; @@ -14,7 +14,7 @@ let in { - options.modules.hm.dev.dotnet = { + options.modules.hm.dev.languages.dotnet = { enable = lib.mkEnableOption "Enable .NET development environment"; sdk-versions = lib.mkOption { type = lib.types.listOf lib.types.str; diff --git a/modules/hm/common/dev/node/node.nix b/modules/hm/common/dev/languages/node.nix similarity index 93% rename from modules/hm/common/dev/node/node.nix rename to modules/hm/common/dev/languages/node.nix index d243733..293ac4d 100644 --- a/modules/hm/common/dev/node/node.nix +++ b/modules/hm/common/dev/languages/node.nix @@ -1,7 +1,7 @@ { pkgs, lib, config, ... }: let - cfg = config.modules.hm.dev.node; + cfg = config.modules.hm.dev.languages.node; expandPath = path: if lib.hasPrefix "~/" path then "${config.home.homeDirectory}/${lib.removePrefix "~/" path}" @@ -27,7 +27,7 @@ let allNodePackages = lib.flatten (map nodeWithPackageManager cfg.versions) ++ (map (pkgName: pkgs.${pkgName}) cfg.extraPackages); in { - options.modules.hm.dev.node = { + options.modules.hm.dev.languages.node = { enable = lib.mkEnableOption "Enable Node.js development environment"; packageManager = lib.mkOption { type = lib.types.enum [ "pnpm" "yarn" "npm" ]; @@ -36,12 +36,11 @@ in }; versions = lib.mkOption { type = lib.types.listOf lib.types.str; - default = [ "20" ]; + default = [ "22" ]; description = "List of Node.js versions to install (e.g. ["18" "20"])"; }; - extraPackages = lib.mkOption { - type = lib.types.listOf lib.types.package; + type = lib.types.listOf lib.types.str; default = []; description = "Additional node packages to install; need to specify nodePackages.{name of package}"; }; diff --git a/modules/hm/common/dev/python.nix b/modules/hm/common/dev/languages/python.nix similarity index 81% rename from modules/hm/common/dev/python.nix rename to modules/hm/common/dev/languages/python.nix index 92ad47e..ad1e549 100644 --- a/modules/hm/common/dev/python.nix +++ b/modules/hm/common/dev/languages/python.nix @@ -2,7 +2,7 @@ let # Shorthand for accessing module config - cfg = config.modules.hm.dev.python; + cfg = config.modules.hm.dev.languages.python; # Function to get the required Python version with pipx, pip, and extra packages pythonWithPipx = version: let @@ -26,7 +26,7 @@ let allPythonPackages = lib.flatten (map pythonWithPipx cfg.versions); in { - options.modules.hm.dev.python = { + options.modules.hm.dev.languages.python = { # Main toggle enable = lib.mkEnableOption "Enable Python development environment"; @@ -55,6 +55,7 @@ in { poetry.enable = lib.mkEnableOption "Install Poetry"; pdm.enable = lib.mkEnableOption "Install PDM"; tools.enable = lib.mkEnableOption "Install dev tools (pytest, black, flake8)"; + shellTools.enable = lib.mkEnableOption "Enable shell tools for Python"; }; # Actual config if module is enabled @@ -63,7 +64,6 @@ in { home = { packages = allPythonPackages - ++ lib.optional cfg.poetry.enable pkgs.poetry ++ lib.optional cfg.pdm.enable pkgs.pdm ++ lib.optionals cfg.tools.enable [ pkgs."python${cfg.defaultVersion}Packages".black @@ -71,21 +71,6 @@ in { pkgs."python${cfg.defaultVersion}Packages".pytest ]; - # Files to write to the home directory - file = lib.mkMerge ( - [ - { - ".config/profile.d/python-aliases.sh".text = '' - export PATH=${pkgs."python${cfg.defaultVersion}"}/bin:$PATH - alias py="python" - alias pipx="pipx" - alias pip="pip" - alias pyclean="find . -type f -name '*.py[co]' -delete" - alias pytest="python -m pytest" - ''; - } - ] - ); # Shell aliases shellAliases = { py = "python"; @@ -93,5 +78,18 @@ in { pytest = "python -m pytest"; }; }; + programs = { + poetry = { + enable = cfg.poetry.enable; + settings = { + virtualenvs.create = true; + virtualenvs.in-project = true; + }; + }; + + pyenv.enableZshIntegration = cfg.shellTools.enable; + zsh.prezto.python.virtualenvAutoSwitch = cfg.shellTools.enable; + pylint.enable = cfg.shellTools.enable; + }; }; } diff --git a/modules/hm/common/dev/rust.nix b/modules/hm/common/dev/languages/rust.nix similarity index 87% rename from modules/hm/common/dev/rust.nix rename to modules/hm/common/dev/languages/rust.nix index a39a730..ba94945 100644 --- a/modules/hm/common/dev/rust.nix +++ b/modules/hm/common/dev/languages/rust.nix @@ -1,7 +1,7 @@ { pkgs, lib, config, ... }: let - cfg = config.modules.hm.dev.rust; + cfg = config.modules.hm.dev.languages.rust; rustDefaultPackages = with pkgs; [rustc cargo rust-analyzer clippy rustfmt]; @@ -11,7 +11,7 @@ let in { - options.modules.hm.dev.rust = { + options.modules.hm.dev.languages.rust = { enable = lib.mkEnableOption "Enable Rust development environment"; extraPackages = lib.mkOption { type = lib.types.listOf lib.types.package; diff --git a/modules/hm/common/dev/node/pm2.nix b/modules/hm/common/dev/node/pm2.nix deleted file mode 100644 index 3f8de7e..0000000 --- a/modules/hm/common/dev/node/pm2.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.dev.node.pm2; -in -{ - options.modules.hm.dev.node.pm2 = { - enable = lib.mkEnableOption "Enable PM2 process manager for Node.js applications"; - }; - - config = lib.mkIf cfg.enable { - home.packages = (with pkgs; [ - pm2 - ]); - }; -} diff --git a/modules/hm/common/dev/global-tools/cli.nix b/modules/hm/common/dev/tools/cli.nix similarity index 86% rename from modules/hm/common/dev/global-tools/cli.nix rename to modules/hm/common/dev/tools/cli.nix index ce51bd4..1934212 100644 --- a/modules/hm/common/dev/global-tools/cli.nix +++ b/modules/hm/common/dev/tools/cli.nix @@ -1,7 +1,7 @@ { pkgs, lib, config, ... }: let - cfg = config.modules.hm.dev.global-tools; + cfg = config.modules.hm.dev.tools; # Map CLI tools to their packages cliToPackage = with pkgs; { @@ -15,7 +15,7 @@ let in { - options.modules.hm.dev.global-tools = { + options.modules.hm.dev.tools = { cli = lib.mkOption { type = lib.types.listOf (lib.types.enum (lib.attrNames cliToPackage)); default = []; diff --git a/modules/hm/common/dev/tools/git-action.nix b/modules/hm/common/dev/tools/git-action.nix new file mode 100644 index 0000000..7c26998 --- /dev/null +++ b/modules/hm/common/dev/tools/git-action.nix @@ -0,0 +1,22 @@ +{ pkgs, lib, config, ... }: + +let + cfg = config.modules.hm.dev.tools.git-action; + + wrkflw = pkgs.callPackage ../../../../wrapper/wrkflw.nix {}; +in +{ + options.modules.hm.dev.tools.git-action = { + packages = lib.mkOption { + type = lib.types.listOf (lib.types.enum ["act" "wrkflw"]); + default = []; + description = "The package to use for running GitHub Actions locally."; + }; + }; + + config = { + home.packages = + (lib.optional (lib.elem "act" cfg.packages) pkgs.act) ++ + (lib.optional (lib.elem "wrkflw" cfg.packages) wrkflw); + }; +} diff --git a/modules/hm/common/dev/tools/gitleaks.nix b/modules/hm/common/dev/tools/gitleaks.nix new file mode 100644 index 0000000..8a4596b --- /dev/null +++ b/modules/hm/common/dev/tools/gitleaks.nix @@ -0,0 +1,14 @@ +{ pkgs, lib, config, ... }: + +let + cfg = config.modules.hm.dev.tools.gitleaks; +in +{ + options.modules.hm.dev.tools = { + gitleaks.enable = lib.mkEnableOption "Enable Gitleaks for detecting secrets in git repositories"; + }; + + config = lib.mkIf cfg.enable { + home.packages = [ pkgs.gitleaks ]; + }; +} \ No newline at end of file diff --git a/modules/hm/common/dev/global-tools/nix.nix b/modules/hm/common/dev/tools/nix.nix similarity index 88% rename from modules/hm/common/dev/global-tools/nix.nix rename to modules/hm/common/dev/tools/nix.nix index c5bc2c8..6999e31 100755 --- a/modules/hm/common/dev/global-tools/nix.nix +++ b/modules/hm/common/dev/tools/nix.nix @@ -1,10 +1,10 @@ { pkgs, lib, config, ... }: let - cfg = config.modules.hm.dev.global-tools.nix; + cfg = config.modules.hm.dev.tools.nix; in { - options.modules.hm.dev.global-tools.nix = { + options.modules.hm.dev.tools.nix = { enable = lib.mkEnableOption "Enable Nix development environment"; }; diff --git a/modules/hm/common/dev/node/prisma.nix b/modules/hm/common/dev/tools/prisma.nix similarity index 88% rename from modules/hm/common/dev/node/prisma.nix rename to modules/hm/common/dev/tools/prisma.nix index 5844966..47dc3e3 100644 --- a/modules/hm/common/dev/node/prisma.nix +++ b/modules/hm/common/dev/tools/prisma.nix @@ -1,10 +1,10 @@ { pkgs, lib, config, ... }: let - cfg = config.modules.hm.dev.node.prisma; + cfg = config.modules.hm.dev.tools.prisma; in { - options.modules.hm.dev.node.prisma = { + options.modules.hm.dev.tools.prisma = { enable = lib.mkEnableOption "Enable Prisma ORM for Node.js applications"; }; diff --git a/modules/hm/common/documentation/default.nix b/modules/hm/common/documentation/default.nix index 1e5d901..00d2b2c 100644 --- a/modules/hm/common/documentation/default.nix +++ b/modules/hm/common/documentation/default.nix @@ -5,7 +5,7 @@ let # Map document editors to their packages editorsToPackage = with pkgs; { - onlyoffice = onlyoffice-bin; + onlyoffice = null; okular = okular; }; @@ -25,5 +25,6 @@ in config = { home.packages = editorsPackages; + programs.onlyoffice.enable = lib.elem "onlyoffice" cfg.editors; }; } \ No newline at end of file diff --git a/modules/hm/common/documentation/obsidian.nix b/modules/hm/common/documentation/obsidian.nix index fa30aa6..a2f3cb4 100644 --- a/modules/hm/common/documentation/obsidian.nix +++ b/modules/hm/common/documentation/obsidian.nix @@ -24,96 +24,29 @@ let }; /*────────────────────────── - │ Git‑based backup script │ + │ Find-based TODO copier │ └──────────────────────────*/ - backupScript = pkgs.writeShellScript "obsidian-backup-git" '' - set -euo pipefail - - REG_FILE="''${XDG_CONFIG_HOME:-$HOME/.config}/obsidian/obsidian.json" - if [ ! -f "$REG_FILE" ]; then - echo "No vault registry found at $REG_FILE — nothing to back up." - exit 0 - fi - - mapfile -t vaults < <(${pkgs.jq}/bin/jq -r '.[].path' "$REG_FILE" | ${pkgs.gawk}/bin/awk '!seen[$0]++') - if [ ''${#vaults[@]} -eq 0 ]; then - echo "Vault registry is empty." - exit 0 - fi - - for VAULT in "''${vaults[@]}"; do - if [ ! -d "$VAULT" ]; then - echo "⚠️ $VAULT missing — skipping" - continue - fi - gitDir="$(find "$VAULT" -maxdepth 2 -type d -name .git | head -n1 || true)" - if [ -z "$gitDir" ]; then - echo "ℹ️ $VAULT is not a git repo — skipping" - continue - fi - - cd "$VAULT" - echo "→ Backing up: $VAULT" - git fetch --quiet origin || true - if git show-ref --verify --quiet refs/heads/temp; then - git checkout temp - else - git checkout -B temp - fi - git add -A - git commit -m "Backup on $(date -u +"%Y-%m-%dT%H:%M:%SZ")" || echo " Nothing new to commit" - if ! git push origin temp; then - echo "⚠️ Failed to push $VAULT to remote" - continue - fi - done - ''; - - /*────────────────────────── - │ TODO‑link monitoring │ - └──────────────────────────*/ - linkScript = pkgs.writeShellScript "obsidian-todo-linker" '' + copyScript = pkgs.writeShellScript "obsidian-todo-copier" ('' OBSIDIAN_DIR="${cfg.projectsDir}" DEV_DIR="${cfg.devDir}" # Create obsidian projects directory if it doesn't exist mkdir -p "$OBSIDIAN_DIR" - mkdir -p "$DEV_DIR" - - # Variable to track last update time - last_update=0 - update_interval=1 # Minimum seconds between updates - - # Function to create/update hardlinks - update_links() { - current_time=$(date +%s) - time_diff=$((current_time - last_update)) - - # Only update if enough time has passed since last update - if [ $time_diff -ge $update_interval ]; then - find "$DEV_DIR" -maxdepth "${toString cfg.searchLinkerDepth}" -name "TODO.md" | while read -r todo_file; do - project_name=$(basename "$(dirname "$todo_file")") - target_link="$OBSIDIAN_DIR/$project_name-todo.md" - - # Remove existing link if it exists - rm -f "$target_link" - # Create new hardlink - ln "$todo_file" "$target_link" - done - last_update=$current_time - fi - } - # Initial link creation - update_links + rm -f "$OBSIDIAN_DIR"/*-todo.md + + # Find all TODO.md files (suppress permission errors) + find "$DEV_DIR" -name "T[Oo][Dd][Oo].md" -type f -readable 2>/dev/null | while read -r todo_file; do + # Get project name from path + dir_path=$(dirname "$todo_file") + project_name=$(basename "$dir_path") + target_file="$OBSIDIAN_DIR/$project_name-todo.md" - # Monitor both directories for changes - ${pkgs.inotify-tools}/bin/inotifywait -m -r -e modify,create,delete,move "$DEV_DIR" "$OBSIDIAN_DIR" | while read -r directory events filename; do - if [[ "$filename" == "TODO.md" ]] || [[ "$filename" == *-todo.md ]]; then - update_links - fi + cat "$todo_file" > "$target_file" + + echo "Copied: $todo_file → $target_file" done - ''; + ''); in { /*────────────────────────── @@ -122,19 +55,9 @@ in options.modules.hm.documentation.obsidian = { enable = lib.mkEnableOption "Enable the Obsidian module"; - backupMethod = lib.mkOption { - type = lib.types.enum [ "none" "git-push-temp" ]; - default = "none"; - description = '' - Backup strategy. - • none – disable backups - • git-push-temp – commit & push vault to the **temp** branch - ''; - }; - projectsDir = lib.mkOption { type = lib.types.str; - default = "${defaultObsiPath}/obsidian/home/Projects"; + default = "${defaultObsiPath}/obsidian/home/content/Projects"; description = "Directory containing Obsidian project files"; }; @@ -147,69 +70,45 @@ in linker = lib.mkOption { type = lib.types.bool; default = true; - description = "Create hard‑links from each project’s TODO.md into the vault"; - }; - - searchLinkerDepth = lib.mkOption { # ← renamed for consistency - type = lib.types.int; - default = 2; - description = "Max depth to search for TODO.md files under devDir"; + description = "Enable automatic TODO.md copying at startup"; }; }; /*────────────────────────── │ Module implementation │ └──────────────────────────*/ - config = lib.mkIf cfg.enable { + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + obsidian + ]; - home.packages = with pkgs; [ obsidian inotify-tools jq gawk ]; + home.sessionVariables.OBSIDIAN_VAULT = "${defaultObsiPath}/obsidian"; - home.sessionVariables.OBSIDIAN_VAULT = "${defaultObsiPath}/obsidian"; - - home.file = { - ".config/hyde/wallbash/Wall-Ways/obsidian.dcol" = { - source = obsidianDcol; force = true; mutable = true; - }; - "${config.home.sessionVariables.OBSIDIAN_VAULT}/home/.obsidian/themes/Wallbash" = { - source = wallbashTheme; recursive = true; force = true; mutable = true; - }; - }; - - home.activation.obsidianBackup = lib.mkIf (cfg.backupMethod == "git-push-temp") ( - lib.hm.dag.entryAfter [ "writeBoundary" ] '' - echo "Running Obsidian backup to temp branch…" - ${backupScript} - '' - ); - - systemd.user = lib.mkIf (cfg.backupMethod == "git-push-temp") { - services.obsidian-backup = { - Unit.Description = "Obsidian vault backup to git temp branch"; - Service = { Type = "oneshot"; ExecStart = "${backupScript}"; StandardOutput = "journal"; StandardError = "journal"; }; - }; - timers.obsidian-backup = { - Unit.Description = "Run Obsidian vault backup every 7 days"; - Timer = { - OnBootSec = "5m"; - OnUnitActiveSec = "7d"; - Persistent = true; - Unit = "obsidian-backup.service"; + home.file = { + ".config/hyde/wallbash/Wall-Ways/obsidian.dcol" = { + source = obsidianDcol; force = true; mutable = true; + }; + "${config.home.sessionVariables.OBSIDIAN_VAULT}/home/content/.obsidian/themes/Wallbash" = { + source = wallbashTheme; recursive = true; force = true; mutable = true; }; - Install.WantedBy = [ "timers.target" ]; }; + } - services.obsidian-todo-linker = lib.mkIf cfg.linker { - Unit.Description = "Link & monitor project TODO files to Obsidian"; + (lib.mkIf cfg.linker { + systemd.user.services.obsidian-todo-copier = { + Unit = { + Description = "Copy project TODO files to Obsidian"; + After = [ "default.target" ]; + }; Service = { - Type = "simple"; - ExecStart = "${linkScript}"; - Restart = "always"; - RestartSec = "5"; + Type = "oneshot"; + ExecStart = "${copyScript}"; StandardOutput = "journal"; - StandardError = "journal"; + StandardError = "journal"; }; Install.WantedBy = [ "default.target" ]; }; - }; - }; -} + }) + ]); +} \ No newline at end of file diff --git a/modules/hm/common/emulator/default.nix b/modules/hm/common/emulator/default.nix deleted file mode 100644 index 1b4e41f..0000000 --- a/modules/hm/common/emulator/default.nix +++ /dev/null @@ -1,82 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.emulator; - - # Map emulators to their packages - emulatorToPackage = with pkgs; { - playonlinux = [ playonlinux ]; - bottles = [ bottles ]; - dosbox = [ dosbox ]; - }; - - # Wine packages based on version - winePackages = with pkgs; { - stable = wine; - wayland = wineWayland; - fonts = wine; - }; - - # Proton packages - protonPackages = with pkgs; [ - protonup-qt - protontricks - ]; - - # Get packages for enabled emulators - baseEmulatorPackages = lib.concatMap (emulator: emulatorToPackage.${emulator} or []) cfg.emulators; - - # Additional packages based on configuration - additionalPackages = with pkgs; [] - ++ lib.optionals cfg.wine.enable [ (winePackages.${cfg.wine.version} or pkgs.wine) winetricks ] - ++ lib.optionals cfg.proton.enable protonPackages; - -in { - options.modules.hm.emulator = { - emulators = lib.mkOption { - type = lib.types.listOf (lib.types.enum ["playonlinux" "proton" "wine" "bottles" "dosbox"]); - default = []; - description = "List of emulators to enable"; - }; - - wine = { - enable = lib.mkOption { - type = lib.types.bool; - default = lib.elem "wine" cfg.emulators; - defaultText = "true if 'wine' is in emulators list"; - description = "Enable Wine Windows compatibility layer"; - }; - - version = lib.mkOption { - type = lib.types.enum ["stable" "wayland" "fonts"]; - default = "stable"; - description = "Wine version to install"; - }; - - prefix = lib.mkOption { - type = lib.types.str; - default = "${config.home.homeDirectory}/.wine"; - description = "Custom WINEPREFIX value"; - }; - }; - - proton = { - enable = lib.mkOption { - type = lib.types.bool; - default = lib.elem "proton" cfg.emulators; - defaultText = "true if 'proton' is in emulators list"; - description = "Enable Proton (Steam Play)"; - }; - }; - }; - - config = { - home.packages = lib.unique (baseEmulatorPackages ++ additionalPackages); - - # Environment variables for Wine/Proton - home.sessionVariables = lib.mkIf (cfg.wine.enable || cfg.proton.enable) { - WINEPREFIX = cfg.wine.prefix; - WINEARCH = "win64"; - }; - }; -} \ No newline at end of file diff --git a/modules/hm/common/extra/ignore-file-retriever.nix b/modules/hm/common/extra/ignore-file-retriever.nix deleted file mode 100644 index eec0e09..0000000 --- a/modules/hm/common/extra/ignore-file-retriever.nix +++ /dev/null @@ -1,157 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.extra.ignore-file-retriever; - - expandPath = path: let - normalized = lib.removeSuffix "/" path; - in if lib.hasPrefix "~/" normalized then - config.home.homeDirectory + (lib.removePrefix "~/" normalized) - else - normalized; - - templatePath = expandPath cfg.templatePath; - outputPath = expandPath cfg.outputPath; - - ignoreFileRetrieverScript = pkgs.writeShellScriptBin "ignore-file-retriever" '' - #!/usr/bin/env bash - set -euo pipefail - - TEMPLATE_FILE="''${TEMPLATE_FILE:-${templatePath}}" - OUTPUT_FILE="''${OUTPUT_FILE:-${outputPath}}" - WORKING_DIR="''${WORKING_DIR:-.}" - - # Verify template exists - if [ ! -f "$TEMPLATE_FILE" ]; then - echo "Error: Template file not found at $TEMPLATE_FILE" >&2 - exit 1 - fi - - # Create output directory if needed - mkdir -p "$(dirname "$OUTPUT_FILE")" - - # Initialize output file with template if it doesn't exist - if [ ! -f "$OUTPUT_FILE" ]; then - cp "$TEMPLATE_FILE" "$OUTPUT_FILE" - echo "Created $OUTPUT_FILE from template" - fi - - # Create temporary file - TMP_FILE="$(mktemp)" - - # Process all .gitignore files recursively - find "$WORKING_DIR" -name ".gitignore" | while read -r gitignore; do - echo "Processing $gitignore" - - # Get relative path from working directory - rel_path="$(realpath --relative-to="$WORKING_DIR" "$(dirname "$gitignore")")" - - # Special case for root directory - if [ "$rel_path" = "." ]; then - rel_path="" - else - rel_path="$rel_path/" - fi - - # Process each pattern - while IFS= read -r pattern; do - # Skip comments and empty lines - [[ "$pattern" =~ ^#|^$ ]] && continue - - # Handle absolute paths - if [[ "$pattern" =~ ^/ ]]; then - echo "$pattern" >> "$TMP_FILE" - continue - fi - - # Handle directory patterns - if [[ "$pattern" =~ /$ ]]; then - echo "''${rel_path}''${pattern}" >> "$TMP_FILE" - else - # Handle normal patterns with proper directory prefix - if [[ "$pattern" =~ / ]]; then - # Pattern contains subdirectories - echo "''${rel_path}''${pattern}" >> "$TMP_FILE" - else - # Simple pattern applies to all levels - echo "**/''${pattern}" >> "$TMP_FILE" - fi - fi - done < "$gitignore" - done - - # Re-assemble final file: template + generated patterns - cat "$TEMPLATE_FILE" > "$OUTPUT_FILE" - cat "$TMP_FILE" >> "$OUTPUT_FILE" - rm "$TMP_FILE" - echo "Updated $OUTPUT_FILE with patterns from .gitignore files" - ''; -in -{ - options.modules.hm.extra.ignore-file-retriever = { - enable = lib.mkEnableOption "Enable ignore file retriever script to create .stignore from .gitignore patterns"; - - templatePath = lib.mkOption { - type = lib.types.str; - default = "~/Templates/gitignore/.stignore.template"; - description = "Path to the template .stignore file"; - }; - - outputPath = lib.mkOption { - type = lib.types.str; - default = "~/Templates/gitignore/.stignore"; - description = "Path to the output .stignore file"; - }; - - watchMode = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Whether to watch for .gitignore changes and auto-update"; - }; - - watchPaths = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - description = "Additional paths to watch for .gitignore changes"; - }; - }; - - config = lib.mkIf cfg.enable { - home.packages = [ - ignoreFileRetrieverScript - pkgs.inotify-tools - ]; - - home.file."${templatePath}".text = lib.mkIf (!lib.pathExists (expandPath cfg.templatePath)) '' - # Syncthing ignore patterns - # Generated from .gitignore files - # (?!).* - # - # This file is automatically generated. Manual changes may be overwritten. - # Add permanent custom patterns to the template file instead. - ''; - - systemd.user.services.ignore-file-watcher = lib.mkIf cfg.watchMode { - Unit = { - Description = "Watch for .gitignore changes and update .stignore"; - After = [ "network.target" ]; - }; - Service = { - ExecStart = let - watchDirs = lib.concatStringsSep " " ( - ["%h"] ++ map expandPath cfg.watchPaths - ); - in - "${pkgs.inotify-tools}/bin/inotifywait -m -r -e modify,move,create,delete \ - --format '%w%f' ${watchDirs} | \ - grep '\.gitignore$' | \ - while read -r path; do \ - WORKING_DIR=\"$(dirname \"$path\")\" ignore-file-retriever; \ - done"; - Restart = "always"; - RestartSec = "5s"; - }; - Install.WantedBy = [ "default.target" ]; - }; - }; -} \ No newline at end of file diff --git a/modules/hm/common/extra/shader-cache-cleanup.nix b/modules/hm/common/extra/shader-cache-cleanup.nix new file mode 100644 index 0000000..42e4746 --- /dev/null +++ b/modules/hm/common/extra/shader-cache-cleanup.nix @@ -0,0 +1,306 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.modules.hm.extra.shader-cache-cleanup; + + # Default Steam library paths + defaultSteamPaths = [ + "\${HOME}/.steam/steam" + "\${HOME}/.local/share/Steam" + ]; + + # The script that handles the cleanup + cleanupScript = pkgs.writeShellScriptBin "cleanup-shader-caches" '' + set -euo pipefail + + # Help function + show_help() { + cat << EOF + Usage: cleanup-shader-caches [OPTIONS] + + Clean up old Steam shader cache files to save disk space. + + Options: + -h, --help Show this help message and exit + -d, --dry-run Show what would be deleted without actually deleting + -v, --verbose Show detailed output + -p, --paths PATHS Override configured Steam paths (comma-separated) + -t, --threshold N Override configured days threshold + --list-paths Show detected Steam paths and exit + --list-caches Show detected shader cache directories and exit + + Examples: + # Dry run to see what would be cleaned + cleanup-shader-caches --dry-run + + # Clean with verbose output + cleanup-shader-caches --verbose + + # Use custom paths and threshold + cleanup-shader-caches --paths "~/.steam/root,~/.local/share/Steam" --threshold 30 + + Configuration (from Nix module): + Days Threshold: ${toString cfg.daysThreshold} days + Steam Paths: ${toString cfg.steamPaths} + File Patterns: ${toString cfg.filePatterns} + Excluded Dirs: ${toString cfg.excludedDirs} + Dry Run: ${if cfg.dryRun then "enabled" else "disabled"} + EOF + } + + # Parse command line arguments + DRY_RUN=${if cfg.dryRun then "true" else "false"} + VERBOSE=false + CUSTOM_PATHS="" + CUSTOM_THRESHOLD="" + LIST_PATHS=false + LIST_CACHES=false + + while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_help + exit 0 + ;; + -d|--dry-run) + DRY_RUN=true + shift + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + -p|--paths) + CUSTOM_PATHS="$2" + shift 2 + ;; + -t|--threshold) + CUSTOM_THRESHOLD="$2" + shift 2 + ;; + --list-paths) + LIST_PATHS=true + shift + ;; + --list-caches) + LIST_CACHES=true + shift + ;; + *) + echo "Unknown option: $1" + show_help + exit 1 + ;; + esac + done + + # Use custom threshold if provided, otherwise use configured value + DAYS_THRESHOLD=''${CUSTOM_THRESHOLD:-${toString cfg.daysThreshold}} + + # Use custom paths if provided, otherwise use configured paths + if [ -n "$CUSTOM_PATHS" ]; then + # Split comma-separated paths + IFS=',' read -ra STEAM_PATHS <<< "$CUSTOM_PATHS" + else + STEAM_PATHS=(${toString cfg.steamPaths}) + fi + + # Find all potential Steam directories + STEAM_DIRS=() + for pattern in "''${STEAM_PATHS[@]}"; do + # Expand ~ and environment variables + expanded_dir=$(eval echo "$pattern") + if [ -d "$expanded_dir" ]; then + STEAM_DIRS+=("$expanded_dir") + if [ "$VERBOSE" = true ] || [ "$LIST_PATHS" = true ]; then + echo "Found Steam directory: $expanded_dir" + fi + elif [ "$VERBOSE" = true ]; then + echo "Steam directory not found: $expanded_dir" + fi + done + + if [ ''${#STEAM_DIRS[@]} -eq 0 ]; then + echo "No Steam directories found. Check your steamPaths configuration." + exit 0 + fi + + if [ "$LIST_PATHS" = true ]; then + echo "Detected Steam directories:" + printf " %s\n" "''${STEAM_DIRS[@]}" + exit 0 + fi + + # Build find command for each directory + FIND_CMD=() + for steam_dir in "''${STEAM_DIRS[@]}"; do + shader_cache_dir="$steam_dir/steamapps/shadercache" + if [ -d "$shader_cache_dir" ]; then + FIND_CMD+=("$shader_cache_dir") + if [ "$VERBOSE" = true ] || [ "$LIST_CACHES" = true ]; then + echo "Found shader cache directory: $shader_cache_dir" + fi + elif [ "$VERBOSE" = true ]; then + echo "Shader cache directory not found: $shader_cache_dir" + fi + done + + if [ "$LIST_CACHES" = true ]; then + echo "Detected shader cache directories:" + printf " %s\n" "''${FIND_CMD[@]}" + exit 0 + fi + + if [ ''${#FIND_CMD[@]} -eq 0 ]; then + echo "No shader cache directories found." + exit 0 + fi + + if [ "$VERBOSE" = true ]; then + echo "Cleaning shader caches in: ''${FIND_CMD[*]}" + echo "Days threshold: $DAYS_THRESHOLD" + echo "File patterns: ${toString cfg.filePatterns}" + echo "Excluded directories: ${toString cfg.excludedDirs}" + echo "Dry run: $DRY_RUN" + fi + + # Build exclude patterns for find command + EXCLUDE_CMD="" + if [ ''${#cfg.excludedDirs[@]} -gt 0 ]; then + for excluded_dir in ''${cfg.excludedDirs[@]}; do + EXCLUDE_CMD+=" -not -path '*/$excluded_dir/*'" + done + fi + + # Build file pattern matching for find command + PATTERN_CMD="" + for pattern in ''${cfg.filePatterns[@]}; do + if [ -z "$PATTERN_CMD" ]; then + PATTERN_CMD="-name '$pattern'" + else + PATTERN_CMD="$PATTERN_CMD -o -name '$pattern'" + fi + done + + # Execute the cleanup + if [ "$DRY_RUN" = true ]; then + echo "=== DRY RUN - No files will be deleted ===" + ACTION="-print" + else + ACTION="-delete" + fi + + # Use eval to properly handle the complex find command + eval "${pkgs.findutils}/bin/find \"''${FIND_CMD[@]}\" \ + -type f \ + \( $PATTERN_CMD \) \ + -mtime +$DAYS_THRESHOLD \ + $EXCLUDE_CMD \ + $ACTION" + + echo "Shader cache cleanup completed." + if [ "$DRY_RUN" = true ]; then + echo "This was a dry run. No files were actually deleted." + echo "Run without --dry-run to perform the actual cleanup." + fi + ''; + +in { + options.modules.hm.extra.shader-cache-cleanup = { + enable = mkEnableOption "Automatic shader cache cleanup for Steam games"; + + daysThreshold = mkOption { + type = types.int; + default = 90; + description = "Delete shader cache files older than this many days"; + }; + + interval = mkOption { + type = types.str; + default = "weekly"; + example = "daily"; + description = "How often to run the cleanup (systemd calendar format)"; + }; + + steamPaths = mkOption { + type = types.listOf types.str; + default = defaultSteamPaths; + description = "List of paths to Steam installations (supports environment variables)"; + }; + + dryRun = mkOption { + type = types.bool; + default = false; + description = "If enabled, only print files that would be deleted without actually deleting them"; + }; + + user = mkOption { + type = types.nullOr types.str; + default = null; + description = "User whose shader caches to clean (defaults to current user if null)"; + }; + + excludedDirs = mkOption { + type = with types; listOf str; + default = []; + description = "List of directory names to exclude from cleanup"; + example = literalExpression '' + ["108600"] # Skip cleaning Skyrim's shader cache + ''; + }; + + filePatterns = mkOption { + type = with types; listOf str; + default = ["*.vkd" "*.bin"]; + description = "File patterns to match for cleanup"; + }; + + runAtStartup = mkEnableOption "Run cleanup at startup"; + + timerConfig = mkOption { + type = types.attrsOf types.str; + default = { + OnCalendar = "weekly"; + Persistent = "true"; + }; + description = "Systemd timer configuration for periodic runs"; + }; + }; + + config = mkIf cfg.enable { + home.packages = [ cleanupScript ]; + + systemd.user = { + services.shader-cache-cleanup = { + Unit = { + Description = "Clean up old Steam shader caches"; + After = [ "network.target" ]; + }; + + Service = let + cmd = "${cleanupScript}/bin/cleanup-shader-caches"; + in { + ExecStart = cmd; + Type = "oneshot"; + SuccessExitStatus = "0 1"; + ${if cfg.user != null then "User" else null} = mkIf (cfg.user != null) cfg.user; + }; + Install = mkIf cfg.runAtStartup { + WantedBy = [ "default.target" ]; + }; + }; + + timers.shader-cache-cleanup = mkIf (cfg.timerConfig != {}) { + Unit = { + Description = "Timer for Steam shader cache cleanup"; + }; + Timer = cfg.timerConfig; + Install = { + WantedBy = [ "timers.target" ]; + }; + }; + }; + }; +} \ No newline at end of file diff --git a/modules/hm/common/extra/syncthing-ignore.nix b/modules/hm/common/extra/syncthing-ignore.nix new file mode 100644 index 0000000..c785093 --- /dev/null +++ b/modules/hm/common/extra/syncthing-ignore.nix @@ -0,0 +1,322 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.modules.hm.extra.syncthing-ignore; + + # Build the find command with excluded paths +buildFindCommand = targetDir: excludedDirs: + let + excludePart = if excludedDirs == null || excludedDirs == [] then + "" + else + " " + (concatMapStrings (dir: "-not -path '*/${dir}/*' ") excludedDirs); + in + ''${pkgs.findutils}/bin/find "$TARGET_DIR" -name ".gitignore" -type f -readable${excludePart} 2>/dev/null || true''; + + # The script that finds Syncthing folders from config.xml and aggregates .gitignore files + ignoreAggregatorScript = pkgs.writeShellScriptBin "aggregate-ignores" '' + #!/usr/bin/env bash + set -euo pipefail + + # Function to expand character classes like [Ll] into multiple patterns + expand_character_classes() { + local pattern="$1" + + # Check if pattern contains simple character classes (not ranges like [0-9]) + if [[ "$pattern" =~ \[[^\]]+\] ]]; then + # Find all simple character classes in the pattern + local results=("$pattern") + local changed=true + + while $changed; do + changed=false + local new_results=() + + for current_pattern in "''${results[@]}"; do + # Look for the first simple character class (without number ranges) + if [[ "$current_pattern" =~ (.*)\[([^0-9\-][^]]*)\](.*) ]]; then + changed=true + local before_class="''${BASH_REMATCH[1]}" + local class_part="''${BASH_REMATCH[2]}" + local after_class="''${BASH_REMATCH[3]}" + + # Only expand if it's not a numeric range pattern + if [[ ! "$class_part" =~ [0-9]-[0-9] ]]; then + # Generate all combinations for each character in this class + for (( i=0; i<''${#class_part}; i++ )); do + local char="''${class_part:$i:1}" + new_results+=("''${before_class}''${char}''${after_class}") + done + else + # It's a numeric range, don't expand + new_results+=("$current_pattern") + fi + else + # No more character classes to expand + new_results+=("$current_pattern") + fi + done + + results=("''${new_results[@]}") + done + + # Output each expanded pattern on a separate line + printf "%s\n" "''${results[@]}" + else + # No character classes to expand, output as-is + echo "$pattern" + fi + } + + # Function to extract folder paths from Syncthing config.xml + extract_syncthing_folders() { + local config_file="$1" + if [[ ! -f "$config_file" ]]; then + echo "Error: Syncthing config file not found: $config_file" >&2 + return 1 + fi + + # Use xmlstarlet to extract folder paths + if command -v ${pkgs.xmlstarlet}/bin/xmlstarlet &> /dev/null; then + ${pkgs.xmlstarlet}/bin/xmlstarlet sel -t -m "//folder" -v "@path" -n "$config_file" 2>/dev/null + else + # Fallback: use grep and sed (less reliable but works) + grep -o 'path="[^"]*"' "$config_file" | sed 's/path="//;s/"$//' + fi + } + + function process_directory() { + local TARGET_DIR="$1" + local STIGNORE_FILE="$2" + + # Handle ~ expansion + if [[ "$TARGET_DIR" == "~" ]]; then + TARGET_DIR="$HOME" + fi + TARGET_DIR="''${TARGET_DIR/#\~/$HOME}" + + # Check if directory exists + if [[ ! -d "$TARGET_DIR" ]]; then + echo "Directory does not exist: $TARGET_DIR" + return + fi + + # Check if .stignore exists, if not create it + if [[ ! -f "$STIGNORE_FILE" ]]; then + echo "No .stignore file found in $TARGET_DIR, creating one." + touch "$STIGNORE_FILE" + fi + + echo "Processing directory: $TARGET_DIR" + + # Build the find command with excluded directories from config + local FIND_CMD="${buildFindCommand "\$TARGET_DIR" cfg.excludedDirs}" + + # Find all .gitignore files automatically (no user interaction) + local IGNORE_FILES=$(eval "$FIND_CMD") + + if [ -z "$IGNORE_FILES" ]; then + echo "No .gitignore files found in $TARGET_DIR." + return + fi + + local found_count + found_count=$(echo "$IGNORE_FILES" | ${pkgs.coreutils}/bin/wc -l) + echo "Found $found_count .gitignore file(s) in $TARGET_DIR." + + echo "Aggregating to $STIGNORE_FILE..." + echo "# Auto-generated from .gitignore files" > "$STIGNORE_FILE" + echo "# Generated on $(date)" >> "$STIGNORE_FILE" + echo "# Source directory: $TARGET_DIR" >> "$STIGNORE_FILE" + echo >> "$STIGNORE_FILE" + + # Process each .gitignore file automatically + while IFS= read -r gitignore; do + local rel_path="''${gitignore#$TARGET_DIR/}" + local dir_path="''${rel_path%/.gitignore}" + + echo "# =========================================" >> "$STIGNORE_FILE" + echo "# From: $rel_path" >> "$STIGNORE_FILE" + echo "# =========================================" >> "$STIGNORE_FILE" + echo >> "$STIGNORE_FILE" + + # Add directory prefix only to non-empty, non-comment lines + if [ "$dir_path" != ".gitignore" ]; then + # Process each line, only add prefix to pattern lines (not comments or empty lines) + while IFS= read -r line; do + # Skip empty lines and comment lines + if [[ -z "$line" || "$line" == \#* ]]; then + echo "$line" >> "$STIGNORE_FILE" + else + # Expand character classes in the pattern + expanded_patterns=$(expand_character_classes "$line") + + # Add directory prefix to each expanded pattern + while IFS= read -r expanded_pattern; do + if [[ -n "$expanded_pattern" ]]; then + echo "$dir_path/$expanded_pattern" >> "$STIGNORE_FILE" + fi + done <<< "$expanded_patterns" + fi + done < "$gitignore" + else + # For root .gitignore, expand character classes but don't add directory prefix + while IFS= read -r line; do + if [[ -z "$line" || "$line" == \#* ]]; then + echo "$line" >> "$STIGNORE_FILE" + else + # Expand character classes in the pattern + expanded_patterns=$(expand_character_classes "$line") + + # Add each expanded pattern + while IFS= read -r expanded_pattern; do + if [[ -n "$expanded_pattern" ]]; then + echo "$expanded_pattern" >> "$STIGNORE_FILE" + fi + done <<< "$expanded_patterns" + fi + done < "$gitignore" + fi + + echo >> "$STIGNORE_FILE" + done <<< "$IGNORE_FILES" + + echo "Done. $STIGNORE_FILE created/updated with content from $found_count .gitignore files." + } + + MODE="$1" + shift + + SYNCONFIG="${cfg.configXmlPath}" + + case "$MODE" in + auto) + # Extract folder paths from Syncthing config + echo "Reading Syncthing folders from: $SYNCONFIG" + FOLDER_PATHS=$(extract_syncthing_folders "$SYNCONFIG") + + if [ -z "$FOLDER_PATHS" ]; then + echo "No Syncthing folders found in config.xml" + exit 0 + fi + + echo "Found Syncthing folders:" + echo "$FOLDER_PATHS" + + while IFS= read -r folder_path; do + if [[ -n "$folder_path" && -d "$folder_path" ]]; then + STIGNORE_FILE="$folder_path/.stignore" + echo "Processing Syncthing folder: $folder_path" + process_directory "$folder_path" "$STIGNORE_FILE" + else + echo "Skipping invalid or non-existent folder: $folder_path" + fi + done <<< "$FOLDER_PATHS" + ;; + + manual) + for TARGET_DIR in "$@"; do + STIGNORE_FILE="$TARGET_DIR/.stignore" + echo "Processing directory: $TARGET_DIR" + process_directory "$TARGET_DIR" "$STIGNORE_FILE" + done + ;; + + *) + echo "Usage: $0 auto|manual [directory...]" + echo " auto: Process all folders from Syncthing config.xml" + echo " manual: Process specified directories" + exit 1 + ;; + esac + ''; + +in { + options.modules.hm.extra.syncthing-ignore = { + enable = mkEnableOption "Syncthing ignore file aggregator"; + + mode = mkOption { + type = types.enum ["auto" "manual"]; + default = "auto"; + description = '' + Operation mode: + - auto: find all Syncthing folders from config.xml and process them + - manual: specify directories to process + ''; + }; + + configXmlPath = mkOption { + type = types.str; + default = "${config.home.homeDirectory}/.config/syncthing/config.xml"; + description = "Path to the Syncthing config.xml file"; + }; + + targetDirs = mkOption { + type = with types; listOf str; + default = []; + description = "Directories to process in manual mode"; + }; + + excludedDirs = mkOption { + type = with types; nullOr (listOf str); + default = []; + description = '' + List of directory names to exclude from .gitignore search. + These directories will be skipped when searching for .gitignore files. + Set to null to disable all exclusions. + ''; + example = literalExpression '' + ["node_modules" "vendor" "dist" "build" ".cache"] + ''; + }; + + runAtStartup = mkEnableOption "Run aggregator at startup"; + + timerConfig = mkOption { + type = types.attrsOf types.str; + default = { + OnCalendar = "weekly"; + Persistent = "true"; + }; + description = "Systemd timer configuration for periodic runs"; + }; + }; + + config = mkIf cfg.enable { + home.packages = [ ignoreAggregatorScript pkgs.xmlstarlet ]; + + systemd.user = { + services.syncthing-ignore-aggregator = { + Unit = { + Description = "Aggregate .gitignore files for Syncthing folders"; + After = mkIf cfg.runAtStartup [ "default.target" ]; + }; + Service = let + cmd = if cfg.mode == "auto" then + "${ignoreAggregatorScript}/bin/aggregate-ignores auto" + else + "${ignoreAggregatorScript}/bin/aggregate-ignores manual ${toString cfg.targetDirs}"; + in { + ExecStart = cmd; + Type = "oneshot"; + SuccessExitStatus = "0 1"; + }; + Install = { + WantedBy = [ "default.target" ]; + }; + }; + + timers.syncthing-ignore-aggregator = mkIf (cfg.timerConfig != {}) { + Unit = { + Description = "Timer for Syncthing ignore file aggregator"; + }; + Timer = cfg.timerConfig; + Install = { + WantedBy = [ "timers.target" ]; + }; + }; + }; + }; +} \ No newline at end of file diff --git a/modules/hm/common/games/games.nix b/modules/hm/common/games/games.nix index d044a24..e42b507 100644 --- a/modules/hm/common/games/games.nix +++ b/modules/hm/common/games/games.nix @@ -9,9 +9,9 @@ let packages = with pkgs; [ prismlauncher jdk17 gcc glibc ]; description = "Minecraft Launcher with PrismLauncher"; }; - northstar = { - packages = with inputs.nix-gaming.packages.${pkgs.system}; [ northstar-proton viper ]; - description = "Northstar (Titanfall 2 mod) with Proton and Viper"; + titanfall2 = { + packages = with inputs.nix-gaming.packages.${pkgs.system}; [ viper (lib.hiPrio northstar-proton) ]; + description = "Titanfall 2 via nix-gaming"; }; roblox = { packages = with inputs.nix-gaming.packages.${pkgs.system}; [ roblox-player ]; @@ -22,15 +22,12 @@ let description = "Rocket League via nix-gaming"; }; star-citizen = { - packages = with inputs.nix-gaming.packages.${pkgs.system}; [ star-citizen ]; + packages = [ inputs.nix-gaming.packages.${pkgs.system}.star-citizen pkgs.lug-helper ]; description = "Star Citizen Launcher"; }; }; - # Get list of game names for the enum type gameNames = builtins.attrNames availableGames; - - # Get packages for enabled games gamePackages = lib.unique (lib.concatMap (game: availableGames.${game}.packages) cfg.enabledGames); in { diff --git a/modules/hm/common/games/mangohud.nix b/modules/hm/common/games/mangohud.nix index d11f6fb..e0fe939 100644 --- a/modules/hm/common/games/mangohud.nix +++ b/modules/hm/common/games/mangohud.nix @@ -24,6 +24,8 @@ in }; config = lib.mkIf cfg.enable { + home.file.".config/MangoHud/MangoHud.conf".enable = lib.mkForce false; + # Enable mangohud programs.mangohud = { enable = true; diff --git a/modules/hm/common/multimedia/editing/image.nix b/modules/hm/common/multimedia/editing/image.nix new file mode 100644 index 0000000..5e50b73 --- /dev/null +++ b/modules/hm/common/multimedia/editing/image.nix @@ -0,0 +1,16 @@ +{ pkgs, lib, config, ... }: + +let + cfg = config.modules.hm.multimedia.editing.image; +in +{ + options.modules.hm.multimedia.editing.image = { + enable = lib.mkEnableOption "Enable image editor"; + }; + + config = lib.mkIf cfg.enable { + home.packages = (with pkgs; [ + krita + ]); + }; +} diff --git a/modules/hm/common/multimedia/openshot-qt.nix b/modules/hm/common/multimedia/editing/video.nix similarity index 67% rename from modules/hm/common/multimedia/openshot-qt.nix rename to modules/hm/common/multimedia/editing/video.nix index 695d98b..3f0a8bf 100644 --- a/modules/hm/common/multimedia/openshot-qt.nix +++ b/modules/hm/common/multimedia/editing/video.nix @@ -1,10 +1,10 @@ { pkgs, lib, config, ... }: let - cfg = config.modules.hm.multimedia.openshot-qt; + cfg = config.modules.hm.multimedia.editing.video; in { - options.modules.hm.multimedia.openshot-qt = { + options.modules.hm.multimedia.editing.video = { enable = lib.mkEnableOption "Enable OpenShot video editor"; }; diff --git a/modules/hm/common/multimedia/gimp.nix b/modules/hm/common/multimedia/gimp.nix deleted file mode 100644 index 95a92e0..0000000 --- a/modules/hm/common/multimedia/gimp.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.multimedia.gimp; -in -{ - options.modules.hm.multimedia.gimp = { - enable = lib.mkEnableOption "Enable GIMP image editor"; - }; - - config = lib.mkIf cfg.enable { - home.packages = (with pkgs; [ - gimp - ]); - }; -} diff --git a/modules/hm/common/multimedia/mpv.nix b/modules/hm/common/multimedia/mpv.nix deleted file mode 100644 index 0dc8479..0000000 --- a/modules/hm/common/multimedia/mpv.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.multimedia.mpv; -in -{ - options.modules.hm.multimedia.mpv = { - enable = lib.mkEnableOption "Enable mpv media player with custom scripts"; - }; - - config = lib.mkIf cfg.enable { - # Configure mpv media player - programs.mpv = { - - # Enable mpv - enable = true; - - # Install custom scripts - scripts = with pkgs; [ - mpvScripts.uosc - ]; - - # Script configuration - scriptOpts."uosc" = { - - # Style of timeline - "timeline_style" = "bar"; - - # Volume to step when scrolling - "volume_step" = 5; - }; - }; - }; -} diff --git a/modules/hm/common/multimedia/parsec.nix b/modules/hm/common/multimedia/parsec.nix deleted file mode 100644 index b9234ba..0000000 --- a/modules/hm/common/multimedia/parsec.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.multimedia.parsec; -in -{ - options.modules.hm.multimedia.parsec = { - enable = lib.mkEnableOption "Enable Parsec for remote desktop access"; - }; - - config = lib.mkIf cfg.enable { - home.packages = (with pkgs; [ - parsec-bin - ]); - }; -} diff --git a/modules/hm/common/multimedia/player.nix b/modules/hm/common/multimedia/player.nix new file mode 100644 index 0000000..8c7944b --- /dev/null +++ b/modules/hm/common/multimedia/player.nix @@ -0,0 +1,64 @@ +{ pkgs, lib, config, ... }: + +let + cfg = config.modules.hm.multimedia.player; + + # Miru package definition + miru = pkgs.callPackage ../../../wrapper/hayase.nix { }; + + # Map document clients to their packages + clientsToPackage = with pkgs; { + mpv = null; + vlc = vlc; + stremio = stremio; + jellyfin = jellyfin-media-player; + plex = plex-desktop; + miru = miru; + }; + + # Get packages for enabled clients + enabledPackages = lib.filter (pkg: pkg != null) + (map (c: clientsToPackage.${c}) cfg.clients); + + # Add jellyfin-rpc if enabled + finalPackages = enabledPackages ++ + (lib.optional cfg.jellyfin.rpc pkgs.jellyfin-rpc); + +in +{ + options.modules.hm.multimedia.player = { + clients = lib.mkOption { + type = lib.types.listOf (lib.types.enum (lib.attrNames clientsToPackage)); + default = []; + description = "List of multimedia player clients to install"; + }; + + jellyfin.rpc = lib.mkEnableOption "Enable Jellyfin RPC support"; + }; + + config = lib.mkIf (cfg.clients != []) { + home.packages = finalPackages; + + # Configure mpv media player if it's in the clients list + programs.mpv = lib.mkIf (lib.elem "mpv" cfg.clients) { + + # Enable mpv + enable = true; + + # Install custom scripts + scripts = with pkgs.mpvScripts; [ + uosc + ]; + + # Script configuration + scriptOpts."uosc" = { + + # Style of timeline + "timeline_style" = "bar"; + + # Volume to step when scrolling + "volume_step" = 5; + }; + }; + }; +} \ No newline at end of file diff --git a/modules/hm/common/multimedia/rambox.nix b/modules/hm/common/multimedia/rambox.nix new file mode 100644 index 0000000..386dbe5 --- /dev/null +++ b/modules/hm/common/multimedia/rambox.nix @@ -0,0 +1,16 @@ +{ pkgs, config, lib, ... }: + +let + cfg = config.modules.hm.multimedia.rambox; +in +{ + options.modules.hm.multimedia.rambox = { + enable = lib.mkEnableOption "Enable Rambox for Workspace Simplifier - a cross-platform application organizing web services into Workspaces similar to browser profiles"; + }; + + config = lib.mkIf cfg.enable { + home.packages = (with pkgs; [ + rambox + ]); + }; +} diff --git a/modules/hm/common/multimedia/remote-desktop.nix b/modules/hm/common/multimedia/remote-desktop.nix new file mode 100644 index 0000000..e23e69e --- /dev/null +++ b/modules/hm/common/multimedia/remote-desktop.nix @@ -0,0 +1,25 @@ +{ pkgs, lib, config, ... }: + +let + cfg = config.modules.hm.multimedia.remote-desktop; + + # Map clients to their packages + clientsToPackage = with pkgs; { + parsec = [ parsec-bin ]; + rustdesk = [ rustdesk ]; + }; + + # Get packages for enabled clients + clientsPackages = lib.concatMap (service: clientsToPackage.${service} or []) cfg.clients; +in +{ + options.modules.hm.multimedia.remote-desktop = { + clients = lib.mkOption { + type = lib.types.listOf (lib.types.enum (lib.attrNames clientsToPackage)); + default = []; + description = "List of remote desktop clients to enable"; + }; + }; + + config.home.packages = clientsPackages; +} diff --git a/modules/hm/common/multimedia/obs.nix b/modules/hm/common/multimedia/streaming.nix similarity index 100% rename from modules/hm/common/multimedia/obs.nix rename to modules/hm/common/multimedia/streaming.nix diff --git a/modules/hm/common/multimedia/stremio.nix b/modules/hm/common/multimedia/stremio.nix deleted file mode 100644 index 092328a..0000000 --- a/modules/hm/common/multimedia/stremio.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.multimedia.stremio; -in -{ - options.modules.hm.multimedia.stremio = { - enable = lib.mkEnableOption "Enable Stremio media center"; - }; - - config = lib.mkIf cfg.enable { - home.packages = (with pkgs; [ - stremio - ]); - }; -} diff --git a/modules/hm/common/network/tunnel.nix b/modules/hm/common/network/tunnel.nix index 21fd85d..87cbc68 100644 --- a/modules/hm/common/network/tunnel.nix +++ b/modules/hm/common/network/tunnel.nix @@ -53,7 +53,7 @@ in { options.modules.hm.network.tunnel = { services = lib.mkOption { type = lib.types.listOf (lib.types.enum (lib.attrNames servicesToPackage)); - default = ["localtunnel"]; + default = []; description = "List of tunneling services to enable"; }; diff --git a/modules/hm/common/shell/btop.nix b/modules/hm/common/shell/btop.nix deleted file mode 100644 index 5298165..0000000 --- a/modules/hm/common/shell/btop.nix +++ /dev/null @@ -1,32 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.shell.btop; -in -{ - options.modules.hm.shell.btop = { - enable = lib.mkEnableOption "Enable btop system monitor"; - }; - - config = lib.mkIf cfg.enable { - # Configure btop - programs.btop = { - - # Enable btop - enable = true; - - # Configuration for btop - settings = { - - # Use default terminal background - theme_background = false; - - # Use vim keys - vim_keys = true; - - # Organise processes as a tree by default - proc_tree = true; - }; - }; - }; -} diff --git a/modules/hm/common/shell/disk-usage.nix b/modules/hm/common/shell/disk-usage.nix index 27d2c18..cc3b4fb 100644 --- a/modules/hm/common/shell/disk-usage.nix +++ b/modules/hm/common/shell/disk-usage.nix @@ -6,7 +6,6 @@ let # Map disk usage tools to their packages toolToPackage = with pkgs; { ncdu = ncdu; - diskonaut = diskonaut; gdu = gdu; dust = dust; parallel-disk-usage = parallel-disk-usage; diff --git a/modules/hm/common/shell/fzf.nix b/modules/hm/common/shell/fzf.nix deleted file mode 100644 index 35e1f31..0000000 --- a/modules/hm/common/shell/fzf.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.shell.fzf; -in -{ - options.modules.hm.shell.fzf = { - enable = lib.mkEnableOption "Enable FZF (fuzzy finder)"; - }; - - config.home.packages = (with pkgs; [ - fzf - ]); -} diff --git a/modules/hm/common/shell/navi.nix b/modules/hm/common/shell/navi.nix deleted file mode 100644 index 4e2ea17..0000000 --- a/modules/hm/common/shell/navi.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.shell.navi; -in -{ - options.modules.hm.shell.navi = { - enable = lib.mkEnableOption "Enable Navi (CLI cheat sheet tool)"; - }; - - config = lib.mkIf cfg.enable { - # Configure navi - programs.navi = { - # Enable navi - enable = true; - }; - }; -} diff --git a/modules/hm/common/shell/ranger.nix b/modules/hm/common/shell/ranger.nix deleted file mode 100644 index 83e988c..0000000 --- a/modules/hm/common/shell/ranger.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ pkgs, lib, config, ... }: - -let - cfg = config.modules.hm.shell.ranger; -in -{ - options.modules.hm.shell.ranger = { - enable = lib.mkEnableOption "Enable Ranger (file manager)"; - }; - - config = lib.mkIf cfg.enable { - home.packages = (with pkgs; [ - ranger - ]); - }; -} diff --git a/modules/hm/common/shell/starship.nix b/modules/hm/common/shell/starship.nix deleted file mode 100644 index 3238df9..0000000 --- a/modules/hm/common/shell/starship.nix +++ /dev/null @@ -1,25 +0,0 @@ -{ - pkgs, - lib, - config, - vars, - ... -}: -let - cfg = config.modules.hm.shell.starship; -in -{ - options.modules.hm.shell.starship = { - enable = lib.mkEnableOption "Enable Starship (cross-shell prompt)"; - }; - - config = lib.mkIf cfg.enable { - - # Install starship if desired - # Configure starship prompt for various shells - programs.starship = { - # Enable starship - enable = true; - }; - }; -} diff --git a/modules/hm/common/shell/tools.nix b/modules/hm/common/shell/tools.nix index 39b1b8a..1633283 100644 --- a/modules/hm/common/shell/tools.nix +++ b/modules/hm/common/shell/tools.nix @@ -13,5 +13,37 @@ in home.packages = (with pkgs; [ tree ]); + + programs = { + ranger.enable = true; + navi = { + # Enable navi + enable = true; + enableZshIntegration = true; + }; + fzf = { + enable = true; + enableZshIntegration = true; + tmux.enableShellIntegration = true; + }; + btop = { + + # Enable btop + enable = true; + + # Configuration for btop + settings = { + + # Use default terminal background + theme_background = false; + + # Use vim keys + vim_keys = true; + + # Organise processes as a tree by default + proc_tree = true; + }; + }; + }; }; } diff --git a/modules/hm/common/utilities/app-launcher.nix b/modules/hm/common/utilities/app-launcher.nix new file mode 100644 index 0000000..5e07ee1 --- /dev/null +++ b/modules/hm/common/utilities/app-launcher.nix @@ -0,0 +1,106 @@ +{ pkgs, lib, config, ... }: + +let + cfg = config.modules.hm.utilities.app-launcher.kando; + + themeRepo = { + owner = "kando-menu"; + repo = "menu-themes"; + tag = "v0.2.0"; + }; + + officialThemes = { + "EVNTech Vache" = { + file = "evntech-vache.zip"; + sha256 = "sha256-TjpoWjofftAwbYOtOCxyYZT0nycfnvJ54jbGOyaKGbk="; + }; + "Hexperiment" = { + file = "hexperiment.zip"; + sha256 = "sha256-Ea3mReYlveh+fLpslSn1M35K2gTjkGHojh+6vdL1//g="; + }; + "KnightForge" = { + file = "knight-forge.zip"; + sha256 = "sha256-MVpO1gAnir7RGHfLfaCtNLJ+b1ZAAMjIJeU0fJiB9os="; + }; + "Minecraft" = { + file = "minecraft.zip"; + sha256 = "sha256-plZ7mVVhh1l1USDmmWmtq4N9M9fhz/jbl1hqmtmafRY="; + }; + "Modified Bent Photon" = { + file = "modified-bent-photon.zip"; + sha256 = "sha256-xSv3HIO+QaKbmknFXi4QTAeHG6I0OJKgFf0PwVFxWMM="; + }; + "Neo Ring" = { + file = "neo-ring.zip"; + sha256 = "sha256-+45ptjzsjSyDIo0WWnZUfN4c6UeG1AV7qDrrCRDro7g="; + }; + "Neon Lights Color" = { + file = "neon-lights-color.zip"; + sha256 = "sha256-5QOJQroqa/lpjKYcP50GXVs7WTToz9BUrbkDHxjiQqw="; + }; + "Nether Labels" = { + file = "nether-labels.zip"; + sha256 = "sha256-rlb/s8R0MpaOv/BHV5s2RasVOAyenldT9LDxi+COqxs="; + }; + "Nord" = { + file = "nord.zip"; + sha256 = "sha256-Zx9Dxwo68soPjinUEZB3WGF3oYwh6mDJ5PL7Am1u+BM="; + }; + }; + + fetchOfficialTheme = name: + let + theme = officialThemes.${name}; + src = pkgs.fetchzip { + url = "https://github.com/${themeRepo.owner}/${themeRepo.repo}/releases/download/${themeRepo.tag}/${theme.file}"; + sha256 = theme.sha256; + stripRoot = false; + }; + themeDir = "${src}/${lib.removeSuffix ".zip" theme.file}"; + in + pkgs.runCommand "kando-theme-${name}" {} '' + mkdir -p $out + + if [ ! -d "${themeDir}" ]; then + echo "Error: Theme directory not found: ${themeDir}" + echo "Contents of extracted zip:" + ls -la "${src}" + exit 1 + fi + + cp -r "${themeDir}"/* $out/ + + if [ ! -f "$out/theme.json5" ]; then + echo "Error: Required file theme.json5 missing in theme" + ls -la $out + exit 1 + fi + ''; + + installThemes = themes: + lib.listToAttrs (map (name: + lib.nameValuePair ".config/kando/menu-themes/${name}" { + source = fetchOfficialTheme name; + recursive = true; + } + ) themes); + +in { + options.modules.hm.utilities.app-launcher.kando = { + enable = lib.mkEnableOption "Kando application launcher"; + + menuThemes = lib.mkOption { + type = with lib.types; listOf (enum (lib.attrNames officialThemes)); + default = []; + description = '' + List of official menu themes to install. + Available themes: ${toString (lib.attrNames officialThemes)} + ''; + }; + }; + + config = lib.mkIf cfg.enable { + home.packages = [ pkgs.kando ]; + home.file = installThemes cfg.menuThemes; + }; +} \ No newline at end of file diff --git a/modules/hm/common/utilities/safety/config.json b/modules/hm/common/utilities/safety/config.json new file mode 100644 index 0000000..aa6e226 --- /dev/null +++ b/modules/hm/common/utilities/safety/config.json @@ -0,0 +1,44 @@ +{ + "dev": { + "timer": { + "ignoreIdleInhibitors": false, + "idleTimeout": 300, + "shortBreakTimeout": 2700, + "longBreakTimeout": 5400, + "shortBreakDuration": 180, + "longBreakDuration": 300 + }, + "notification": { + "showProgressBar": true, + "minimumUpdateDelay": 1 + } + }, + "game": { + "timer": { + "ignoreIdleInhibitors": true, + "idleTimeout": 600, + "shortBreakTimeout": 5400, + "longBreakTimeout": 10800, + "shortBreakDuration": 120, + "longBreakDuration": 480 + }, + "notification": { + "showProgressBar": true, + "minimumUpdateDelay": 1 + } + }, + "safety": { + "timer": { + "ignoreIdleInhibitors": false, + "idleTimeout": 240, + "shortBreakTimeout": 1200, + "longBreakTimeout": 3840, + "shortBreakDuration": 120, + "longBreakDuration": 240 + }, + "notification": { + "showProgressBar": true, + "minimumUpdateDelay": 1 + } + } +} \ No newline at end of file diff --git a/modules/hm/common/utilities/safety/ianny.nix b/modules/hm/common/utilities/safety/ianny.nix new file mode 100644 index 0000000..9273cd5 --- /dev/null +++ b/modules/hm/common/utilities/safety/ianny.nix @@ -0,0 +1,252 @@ +{ pkgs, lib, config, ... }: + +let + cfg = config.modules.hm.utilities.safety.ianny; + + # Import presets from JSON file + presets = builtins.fromJSON (builtins.readFile ./config.json); + + # Function to generate TOML content with preset name comment + generateToml = name: settings: '' + # Ianny configuration + # preset "${name}" + + [timer] + # Enabling this will only consider user input alone for idle state, e.g. you will not have breaks when watching videos or playing music without any user input. + ignore_idle_inhibitors = ${lib.boolToString settings.timer.ignoreIdleInhibitors} + + # Timer will stop and reset when you are idle for this amount of seconds. + idle_timeout = ${toString settings.timer.idleTimeout} + + # Active duration that activates a break. + short_break_timeout = ${toString settings.timer.shortBreakTimeout} + long_break_timeout = ${toString settings.timer.longBreakTimeout} + + # Breaks duration. + short_break_duration = ${toString settings.timer.shortBreakDuration} + long_break_duration = ${toString settings.timer.longBreakDuration} + + [notification] + show_progress_bar = ${lib.boolToString settings.notification.showProgressBar} + # Minimum delay of updating the progress bar (lower than 1s may return an error). + minimum_update_delay = ${toString settings.notification.minimumUpdateDelay} + ''; + + # Create preset files + presetFiles = lib.mapAttrs' (name: preset: + lib.nameValuePair ".config/io.github.zefr0x.ianny/preset/${name}.toml" { + text = generateToml name preset; + } + ) presets; + + # Custom configuration file content + customConfig = generateToml "custom" cfg.settings; + + # Rofi-based selector script content with --game support + selectorScriptContent = '' + #!/usr/bin/env bash + set -euo pipefail + + PRESET_DIR="$HOME/.config/io.github.zefr0x.ianny/preset" + CONFIG_FILE="$HOME/.config/io.github.zefr0x.ianny/config.toml" + GAME_PRESET="game" + DEFAULT_PRESET="${cfg.defaultPreset or ""}" + + if [[ ! -d "$PRESET_DIR" ]]; then + notify-send -a "Ianny" "No preset directory found at $PRESET_DIR" + exit 1 + fi + + mapfile -t PRESETS < <(find "$PRESET_DIR" -maxdepth 1 -name "*.toml" -exec basename {} .toml \; | sort) + + if [[ ''${#PRESETS[@]} -eq 0 ]]; then + notify-send -a "Ianny" "No presets available in $PRESET_DIR" + exit 1 + fi + + if [[ "''${1:-}" == "--game" ]]; then + if [[ ! " ''${PRESETS[*]} " =~ " $GAME_PRESET " ]]; then + notify-send -a "Ianny" "Game preset not available" + exit 1 + fi + + # Check current config by reading the preset name comment + if [[ -f "$CONFIG_FILE" ]]; then + current_preset=$(grep "^# preset \"" "$CONFIG_FILE" | head -1 | sed 's/^# preset "\(.*\)"$/\1/') + if [[ "$current_preset" == "$GAME_PRESET" ]]; then + current="$GAME_PRESET" + else + current="other" + fi + else + current="" + fi + + if [[ "$current" == "$GAME_PRESET" ]]; then + # Toggle off: restore defaultPreset or show menu + if [[ -n "$DEFAULT_PRESET" && -f "$PRESET_DIR/$DEFAULT_PRESET.toml" ]]; then + cp "$PRESET_DIR/$DEFAULT_PRESET.toml" "$CONFIG_FILE" + systemctl --user restart ianny + notify-send -a "Ianny" "Preset '$DEFAULT_PRESET' restored" + else + # Show menu if no default + exec "$0" + fi + else + # Save current config as backup if not already game mode + if [[ -f "$CONFIG_FILE" && "$current" != "$GAME_PRESET" ]]; then + cp "$CONFIG_FILE" "$CONFIG_FILE.backup" + fi + cp "$PRESET_DIR/$GAME_PRESET.toml" "$CONFIG_FILE" + systemctl --user restart ianny + notify-send -a "Ianny" "Game preset activated" + fi + exit 0 + fi + + selected=$( + printf '%s\n' "''${PRESETS[@]}" | rofi -dmenu -p "Select Ianny preset:" + ) + + if [[ -z "$selected" ]]; then + exit 0 + fi + + if [[ "''${1:-}" == "--auto" ]]; then + cp "$PRESET_DIR/$selected.toml" "$CONFIG_FILE" + systemctl --user restart ianny + notify-send -a "Ianny" "Preset '$selected' activated" + exit 0 + fi + + confirm=$(printf "Yes\nNo" | rofi -dmenu -p "Activate '$selected'?") + if [[ "$confirm" == "Yes" ]]; then + cp "$PRESET_DIR/$selected.toml" "$CONFIG_FILE" + systemctl --user restart ianny + notify-send -a "Ianny" "Preset '$selected' activated" + fi + ''; +in +{ + options.modules.hm.utilities.safety.ianny = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Enable Ianny break reminder"; + }; + + mode = lib.mkOption { + type = lib.types.enum [ "preset" "custom" ]; + default = "preset"; + description = '' + Operation mode: + - preset: Use predefined preset (can select multiple) + - custom: Fully custom configuration + ''; + }; + + presets = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ "dev" ]; + description = "Which presets to make available"; + }; + + defaultPreset = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Which preset to activate (null for custom)"; + }; + + startAtLogin = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Start Ianny at login"; + }; + + settings = { + timer = { + ignoreIdleInhibitors = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Only consider user input for idle state"; + }; + idleTimeout = lib.mkOption { + type = lib.types.int; + default = 240; + }; + shortBreakTimeout = lib.mkOption { + type = lib.types.int; + default = 1200; + }; + longBreakTimeout = lib.mkOption { + type = lib.types.int; + default = 3840; + }; + shortBreakDuration = lib.mkOption { + type = lib.types.int; + default = 120; + }; + longBreakDuration = lib.mkOption { + type = lib.types.int; + default = 240; + }; + }; + notification = { + showProgressBar = lib.mkOption { + type = lib.types.bool; + default = true; + }; + minimumUpdateDelay = lib.mkOption { + type = lib.types.int; + default = 1; + }; + }; + }; + }; + + config = lib.mkIf cfg.enable { + home.packages = [ + pkgs.ianny + ]; + + home.file = lib.mkMerge [ + (lib.mkIf (cfg.mode == "custom") { + ".config/io.github.zefr0x.ianny/config.toml".text = customConfig; + }) + (lib.mkIf (cfg.mode == "preset") presetFiles) + { + ".local/bin/ianny-preset-selector" = { + text = selectorScriptContent; + executable = true; + force = true; + mutable = true; + }; + } + ]; + + home.activation = lib.mkIf (cfg.mode == "preset" && cfg.defaultPreset != null) { + iannySetup = lib.hm.dag.entryAfter ["writeBoundary"] '' + if [[ -d "$HOME/.config/io.github.zefr0x.ianny/preset" ]]; then + cp "$HOME/.config/io.github.zefr0x.ianny/preset/${cfg.defaultPreset}.toml" \ + "$HOME/.config/io.github.zefr0x.ianny/config.toml" || true + fi + ''; + }; + + systemd.user.services.ianny = { + Unit = { + Description = "ianny - Safety net for Hydenix desktop"; + After = [ "graphical-session.target" ]; + PartOf = [ "graphical-session.target" ]; + }; + Service = { + ExecStart = "${pkgs.ianny}/bin/ianny"; + Restart = "always"; + RestartSec = 5; + }; + Install = lib.mkIf cfg.startAtLogin { + WantedBy = [ "graphical-session.target" ]; + }; + }; + }; +} \ No newline at end of file diff --git a/modules/hm/common/utilities/scalar.nix b/modules/hm/common/utilities/scalar.nix index 79de38a..8236104 100644 --- a/modules/hm/common/utilities/scalar.nix +++ b/modules/hm/common/utilities/scalar.nix @@ -4,70 +4,14 @@ with lib; let cfg = config.modules.hm.utilities.scalar; - - scalar-deb = pkgs.fetchurl { - url = "https://download.scalar.com/linux/deb/x64"; - sha256 = "sha256-R/pf8Df3qYufiDKwTJLEztfcqJ9/+nXclHQqQi+uF3A="; - }; - - scalar-app = pkgs.stdenv.mkDerivation { - name = "scalar-desktop"; - src = scalar-deb; - - nativeBuildInputs = [ pkgs.autoPatchelfHook pkgs.dpkg pkgs.makeWrapper ]; - buildInputs = with pkgs; [ - alsa-lib - mesa - libdrm - libglvnd - vulkan-loader - libva - gtk3 - libsecret - nss - xorg.libXdamage - xorg.libXtst - xorg.libXcomposite - xorg.libXrandr - ]; - - unpackPhase = "dpkg -x $src ."; - - installPhase = '' - # Install application - mkdir -p $out/opt/Scalar - cp -r opt/Scalar/* $out/opt/Scalar/ - - # Install desktop file and icons from usr/share - mkdir -p $out/share - cp -r usr/share/* $out/share/ - - # Fix the Exec path in the desktop file - substituteInPlace $out/share/applications/scalar-app.desktop \ - --replace "Exec=/opt/Scalar/scalar-app" "Exec=$out/bin/scalar-desktop" - - # Create binary wrapper with proper library paths - mkdir -p $out/bin - makeWrapper $out/opt/Scalar/scalar-app $out/bin/scalar-desktop \ - --prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath (with pkgs; [ - alsa-lib - mesa - libdrm - libglvnd - vulkan-loader - libva - ])} \ - --prefix PATH : ${lib.makeBinPath [ pkgs.xorg.xrandr ]} - ''; - }; - + scalarApp = (import ../../../wrapper/scalar.nix { inherit pkgs lib config; }).scalarApp; in { options.modules.hm.utilities.scalar = { enable = mkEnableOption "Scalar desktop application"; }; config = mkIf cfg.enable { - home.packages = [ scalar-app ]; + home.packages = [ scalarApp ]; xdg.enable = true; }; } \ No newline at end of file diff --git a/modules/hm/desktops/configHydenix.nix b/modules/hm/desktops/configHydenix.nix index ca82de2..d3e9f04 100644 --- a/modules/hm/desktops/configHydenix.nix +++ b/modules/hm/desktops/configHydenix.nix @@ -1,16 +1,45 @@ -{ lib }: +{ lib, config }: +let + cfg = config.desktops.hydenix; + utilities = config.modules.hm.utilities; + + iannyOptions = utilities.safety.ianny; + + kandoEnabled = utilities.app-launcher.kando.enable; + + # Generate random command logic + randomCommand = + if cfg.randomOnBoot.wallpaper && cfg.randomOnBoot.theme then + "random-theme.sh -a" + else if cfg.randomOnBoot.wallpaper then + "wallpaper.sh -r" + else if cfg.randomOnBoot.theme then + "random-theme.sh -r" + else + null; + + startupCmds = [ + (lib.optionalString (randomCommand != null) randomCommand) + "sleep 1" + (lib.optionalString kandoEnabled "kando") + ]; + filteredCmds = lib.filter (x: x != "") startupCmds; + execCmd = lib.concatStringsSep " && " filteredCmds; +in { - hyprlandKeybindsConvert = '' + hyprlandKeybinds = '' ## █▄▀ █▀▀ █▄█ █▄▄ █ █▄░█ █▀▄ █ █▄░█ █▀▀ █▀ ## █░█ ██▄ ░█░ █▄█ █ █░▀█ █▄▀ █ █░▀█ █▄█ ▄█ # $scrPath=$HOME/.local/lib/hyde # set scripts path - $l=Launcher $d=[$l|Rofi menus] bindd = $mainMod, colon, $d keybindings hint, exec, pkill -x rofi || $scrPath/keybinds_hint.sh c # launch keybinds hint bindd = $mainMod, semicolon , $d glyph picker , exec, pkill -x rofi || $scrPath/glyph-picker.sh # launch glyph picker + ${lib.optionalString kandoEnabled '' + bindd = Alt, Space, $d launch Kando app launcher, global, menu.kando.Kando:hypr + ''} $ws=Workspaces $d=[$ws|Navigation] @@ -51,6 +80,43 @@ bindd = $mainMod Alt, ccedilla, $d move to workspace 9 (silent), movetoworkspacesilent, 9 bindd = $mainMod Alt, agrave, $d move to workspace 10 (silent), movetoworkspacesilent, 10 + $ws=Modes + $d=[$ws|Safety] + ${lib.optionalString iannyOptions.enable '' + bindd = $mainMod, F1, $d toggle safety mode, exec, toggle-ianny + ${ lib.optionalString (iannyOptions.mode == "preset") '' + bindd = $mainMod, F2, $d select Ianny preset, exec, ianny-preset-selector + ${lib.optionalString (builtins.elem "game" iannyOptions.presets) '' + bindd = $mainMod Alt, G, $d activate game preset, exec, ianny-preset-selector --game + ''} + ''} + ''} + bindd = $mainMod Alt, F4, $d emergency shutdown all apps, exec, pkill -KILL -u $USER + $d=#! unset the group name ''; + + exec-once = '' + exec-once = ${execCmd} + ''; + + config = '' + ${lib.optionalString kandoEnabled '' + ### kando Section + input { + special_fallthrough = true # having only floating windows in the special workspace will not block focusing windows in the regular workspace. + focus_on_close = 1 # focus will shift to the window under the cursor. + } + + windowrule = noblur, class:kando + windowrule = opaque, class:kando + windowrule = size 100% 100%, class:kando + windowrule = noborder, class:kando + windowrule = noanim, class:kando + windowrule = float, class:kando + windowrule = pin, class:kando + + ### + ''} + ''; } \ No newline at end of file diff --git a/modules/hm/desktops/hydenix.nix b/modules/hm/desktops/hydenix.nix index 84f5051..d7c5d60 100644 --- a/modules/hm/desktops/hydenix.nix +++ b/modules/hm/desktops/hydenix.nix @@ -10,13 +10,17 @@ with lib; let cfg = config.desktops.hydenix; - configHydenix = import ./configHydenix.nix { inherit lib; }; + configHydenix = import ./configHydenix.nix { inherit lib config; }; + + # Validate hostnames + validHostnames = [ "fern" "oak" "pine" "cedar" "sapling" "clover" ]; in { imports = [ ../common inputs.hydenix.lib.homeModules + inputs.nix-index-database.homeModules.nix-index ]; options.desktops.hydenix = { @@ -31,59 +35,94 @@ in description = "Hostname for Hydenix desktop, used to determine userprefs.conf"; example = "fern"; }; + + theme = { + themes = mkOption { + type = types.listOf types.str; + default = [ + "Another World" + "Cat Latte" + "Catppuccin Latte" + "Catppuccin Mocha" + "Crimson Blade" + "Ever Blushing" + "Greenify" + "One Dark" + "Oxo Carbon" + "Pixel Dream" + "Rain Dark" + "Rosé Pine" + "Sci-fi" + "Tokyo Night" + ]; + description = "List of available themes for Hydenix desktop"; + }; + }; + + randomOnBoot = { + wallpaper = mkOption { + type = types.bool; + default = false; + description = "Enable random wallpaper on boot"; + }; + + theme = mkOption { + type = types.bool; + default = true; + description = "Enable random theme on boot (requires random wallpaper to be enabled)"; + }; + }; }; config = mkIf cfg.enable { - assertions = [ { - assertion = builtins.elem cfg.hostname [ - "fern" # Desktop - "oak" # Laptop - "pine" # Media - "cedar" # Server - "sapling" # Live USB OS - "clover" # Main VM - ]; - message = "Hostname must be one of: fern, oak, pine, cedar, sapling, clover"; + assertion = builtins.elem cfg.hostname validHostnames; + message = "Hostname must be one of: ${toString validHostnames}. Got: ${cfg.hostname}"; + } + { + assertion = cfg.theme.themes != []; + message = "Theme list cannot be empty"; } ]; hydenix.hm = { enable = true; - gaming.enable = false; + dolphin.enable = cfg.enable; editors = { + enable = cfg.enable; vscode.enable = false; neovim = false; }; - social = { - webcord.enable = false; - vesktop.enable = false; - }; + firefox.enable = cfg.enable; git = { enable = true; name = "mirage"; email = "119869686+ClementBobin@users.noreply.github.com"; }; + hyde.enable = cfg.enable; + hyprland.enable = cfg.enable; + lockscreen.enable = cfg.enable; + notifications.enable = cfg.enable; + qt.enable = cfg.enable; + rofi.enable = cfg.enable; + screenshots.enable = cfg.enable; + social = { + enable = cfg.enable; + discord.enable = false; + webcord.enable = false; + vesktop.enable = cfg.enable; + }; + spotify.enable = cfg.enable; + swww.enable = cfg.enable; theme = { + enable = cfg.enable; active = "Tokyo Night"; - themes = [ - "Another World" - "Cat Latte" - "Catppuccin Latte" - "Catppuccin Mocha" - "Crimson Blade" - "Ever Blushing" - "Greenify" - "One Dark" - "Oxo Carbon" - "Pixel Dream" - "Rain Dark" - "Rosé Pine" - "Sci-fi" - "Tokyo Night" - ]; + themes = cfg.theme.themes; }; + waybar.enable = cfg.enable; + wlogout.enable = cfg.enable; + xdg.enable = cfg.enable; }; home.file = { @@ -93,7 +132,8 @@ in kb_layout = fr } - ${configHydenix.hyprlandKeybindsConvert} + ${configHydenix.config} + ${configHydenix.hyprlandKeybinds} # Example monitor configuration # Replace names like HDMI-A-1, DP-1, etc. with the actual names of your monitors (use `hyprctl monitors` to list) @@ -120,15 +160,27 @@ in # Alt + Enter to toggle fullscreen bind = ALT, Return, fullscreen, 0 + # Alt + Tab to cycle between fullscreen windows bind = ALT, Tab, cyclenext bind = ALT, Tab, bringactivetotop - exec-once = sleep 1 && bitwarden + bind = $mainMod+Ctrl, F, exec, rog-control-center + bind = $mainMod Alt, G, exec, powermode-toggle.sh + + ${configHydenix.exec-once} ''; force = true; mutable = true; }; + ".local/bin/powermode-toggle.sh" = { + source = ./powermode-toggle.sh; + executable = true; + }; + ".local/bin/random-theme.sh" = mkIf cfg.randomOnBoot.theme { + source = ./random-theme.sh; + executable = true; + }; }; }; -} +} \ No newline at end of file diff --git a/modules/hm/desktops/powermode-toggle.sh b/modules/hm/desktops/powermode-toggle.sh new file mode 100644 index 0000000..bddc074 --- /dev/null +++ b/modules/hm/desktops/powermode-toggle.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# Toggle between Performance and default profile + +# Configuration +DEFAULT_PROFILE="Balanced" +PERFORMANCE_PROFILE="Performance" +CONFIG_DIR="$HOME/.config/power-toggle" +LAST_PROFILE_FILE="$CONFIG_DIR/last_profile" + +# Ensure config directory exists +mkdir -p "$CONFIG_DIR" + +# Function to get current profile +get_current_profile() { + asusctl profile -p 2>/dev/null | awk '/Active profile is/ {print $4}' +} + +# Function to set profile +set_profile() { + local profile="$1" + asusctl profile -P "$profile" +} + +# Main toggle function +toggle_performance() { + local current_profile + current_profile=$(get_current_profile) + + # If we're already on Performance, switch back to last saved profile or default + if [ "$current_profile" = "$PERFORMANCE_PROFILE" ]; then + local target_profile + + if [ -f "$LAST_PROFILE_FILE" ]; then + target_profile=$(cat "$LAST_PROFILE_FILE") + echo "Switching back to saved profile: $target_profile" + rm -f "$LAST_PROFILE_FILE" + else + target_profile="$DEFAULT_PROFILE" + echo "Switching to default profile: $target_profile" + fi + + set_profile "$target_profile" + + else + # We're not on Performance, so save current and switch to Performance + echo "$current_profile" > "$LAST_PROFILE_FILE" + echo "Switching to Performance profile" + set_profile "$PERFORMANCE_PROFILE" + fi + + # Show new status + echo "Current profile: $(get_current_profile)" +} + +# Handle command line arguments +case "${1:-}" in + reset) + rm -f "$LAST_PROFILE_FILE" + echo "Reset saved profile. Will use default ($DEFAULT_PROFILE) next time." + ;; + help|--help|-h) + echo "Power Profile Toggle Script" + echo "Usage: $0 [command]" + echo "" + echo "Commands:" + echo " (no command) Toggle between Performance and saved/default profile" + echo " reset Reset saved profile" + echo " help Show this help message" + ;; + *) + toggle_performance + ;; +esac \ No newline at end of file diff --git a/modules/hm/desktops/random-theme.sh b/modules/hm/desktops/random-theme.sh new file mode 100644 index 0000000..c9f8aa1 --- /dev/null +++ b/modules/hm/desktops/random-theme.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash + +THEMES_DIR="$HOME/.config/hyde/themes" + +pick_random_theme() { + mapfile -t THEMES < <(ls -1 "$THEMES_DIR" 2>/dev/null) + if [ ${#THEMES[@]} -eq 0 ]; then + echo "No themes found in $THEMES_DIR" >&2 + return 1 + fi + echo "${THEMES[$RANDOM % ${#THEMES[@]}]}" +} + +show_help() { + cat < [options]" + echo "" + echo "Commands:" + echo " benchmark [duration] Run power consumption benchmarks" + echo " tune Tune power settings" + echo "" + echo "Examples:" + echo " power-tools benchmark 60" + echo " power-tools tune profile max-powersave" + echo " power-tools tune status" + echo "" + echo "For detailed help:" + echo " power-tools benchmark --help" + echo " power-tools tune help" + ;; + esac + ''; in { options.modules.system.hardware.powersave = { enable = lib.mkEnableOption "Enable power saving configuration"; @@ -12,12 +48,18 @@ in { description = "Select the architecture for power saving optimizations"; }; + enableBenchmarkTools = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Install power benchmarking and tuning tools"; + }; + batteryHealth = { enable = lib.mkEnableOption "Enable battery health preservation features"; chargeThresholds = { start = lib.mkOption { type = lib.types.int; - default = 75; + default = cfg.batteryHealth.chargeThresholds.stop - 5; description = "Start charging when battery falls below this percentage"; }; stop = lib.mkOption { @@ -28,12 +70,27 @@ in { }; }; + forcePerfOnAC = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Force performance mode when on AC power (overrides power profile daemon)"; + }; + disk = lib.mkOption { type = lib.types.listOf lib.types.str; default = []; description = "List of disks for power management"; }; + nvidiaPowerManagement = { + enable = lib.mkEnableOption "Enable NVIDIA power management features"; + dynamicBoost = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Enable dynamic boost for NVIDIA GPUs"; + }; + }; + managePowerProfiles = lib.mkOption { type = lib.types.bool; default = true; @@ -49,6 +106,16 @@ in { default = false; description = "Enable ASUS hardware support for power management."; }; + + armouryCrate = lib.mkOption { + type = lib.types.bool; + default = cfg.asus.enable; + description = "Enable Armoury Crate for ASUS hardware management."; + }; + }; + + override = { + thermald = lib.mkEnableOption "Overide thermald params"; }; }; @@ -58,7 +125,12 @@ in { powertop acpi ] ++ lib.optional (cfg.architecture == "amd") amdctl - ++ lib.optionals cfg.asus.enable [ asusctl supergfxctl ]; + ++ lib.optionals cfg.asus.enable [ asusctl supergfxctl ] + ++ lib.optionals cfg.enableBenchmarkTools [ + power-tools + bc # Required for calculations in scripts + iw # Required for WiFi power management + ]; # Enable power management powerManagement = { @@ -75,16 +147,25 @@ in { diskSettings = lib.concatStringsSep " " (lib.genList (_: "128") (lib.length cfg.disk)); in { # CPU settings + CPU_SCALING_GOVERNOR_ON_AC = "performance"; CPU_SCALING_GOVERNOR_ON_BAT = "powersave"; CPU_ENERGY_PERF_POLICY_ON_BAT = "power"; + CPU_ENERGY_PERF_POLICY_ON_AC = "performance"; CPU_BOOST_ON_BAT = 0; + CPU_BOOST_ON_AC=1; # Architecture specific settings CPU_HWP_ON_BAT = if cfg.architecture == "amd" then "power" else "balance_performance"; + + MEM_SLEEP_ON_AC="s2idle"; + MEM_SLEEP_ON_BAT="deep"; + PLATFORM_PROFILE_ON_BAT = "low-power"; + PLATFORM_PROFILE_ON_AC = if cfg.forcePerfOnAC then "performance" else "balanced"; # PCIe power management PCIE_ASPM_ON_BAT = "powersupersave"; + PCIE_ASPM_ON_AC = "performance"; # WiFi power saving WIFI_PWR_ON_BAT = "on"; @@ -103,6 +184,10 @@ in { # Runtime power management RUNTIME_PM_ON_BAT = "auto"; + RUNTIME_PM_ALL = 1; + + # NVIDIA GPU power management + RUNTIME_PM_DRIVER_BLACKLIST = lib.mkIf cfg.nvidiaPowerManagement.enable "nvidia"; # Explicitly set charge thresholds for all batteries # Set charge thresholds for primary battery (BAT0) @@ -153,11 +238,15 @@ in { ] else [ "intel_idle.max_cstate=4" "processor.max_cstate=5" - ]); + ]) ++ + (if cfg.asus.armouryCrate then [ + "i915.enable_dpcd_backlight=1" + "nvidia.NVreg_EnableBacklightHandler=0" + ] else []); # Services configuration services = { - thermald.enable = lib.mkDefault (cfg.architecture == "intel"); + thermald.enable = lib.mkDefault (cfg.architecture == "intel" || cfg.override.thermald); power-profiles-daemon.enable = lib.mkForce (!(cfg.batteryHealth.enable || cfg.managePowerProfiles)); upower.enable = true; @@ -168,6 +257,29 @@ in { }; supergfxd = lib.mkIf cfg.asus.enable { enable = true; + # settings = { + # mode = "Hybrid"; + # vfio_enable = false; + # always_reboot = false; + # no_logind = false; + # logout_timeout_s = 20; + # }; + }; + }; + + hardware.nvidia = lib.mkIf cfg.nvidiaPowerManagement.enable { + dynamicBoost.enable = cfg.nvidiaPowerManagement.dynamicBoost; + }; + + # Create symlinks to individual scripts for direct access + environment.etc = lib.mkIf cfg.enableBenchmarkTools { + "power-scripts/power-benchmark.sh" = { + source = power-benchmark; + mode = "0755"; + }; + "power-scripts/power-tuning.sh" = { + source = power-tuning; + mode = "0755"; }; }; }; diff --git a/modules/system/common/hardware/powersave/power-benchmark.sh b/modules/system/common/hardware/powersave/power-benchmark.sh new file mode 100644 index 0000000..5891f17 --- /dev/null +++ b/modules/system/common/hardware/powersave/power-benchmark.sh @@ -0,0 +1,247 @@ +#!/usr/bin/env bash + +# Power Benchmarking Script +# This script helps benchmark different power settings to find optimal configurations + +set -euo pipefail + +LOGFILE="/tmp/power-benchmark-$(date +%Y%m%d-%H%M%S).log" +DURATION=${1:-60} # Default benchmark duration in seconds + +# Force US locale for consistent number formatting +export LC_NUMERIC="en_US.UTF-8" + +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE" +} + +get_power_usage() { + # Try multiple methods to get power consumption + local power_uw=0 + + # Find all battery interfaces + local battery_paths=() + for bat_path in /sys/class/power_supply/BAT*; do + if [[ -d "$bat_path" ]]; then + battery_paths+=("$bat_path") + fi + done + + # Method 1: Check battery power draw (microwatts) + for bat_path in "${battery_paths[@]}"; do + if [[ -f "${bat_path}/power_now" ]]; then + power_uw=$(cat "${bat_path}/power_now" 2>/dev/null || echo 0) + [[ $power_uw -gt 0 ]] && break + fi + done + + # Method 2: Calculate from current and voltage + if [[ $power_uw -eq 0 ]]; then + for bat_path in "${battery_paths[@]}"; do + if [[ -f "${bat_path}/current_now" && -f "${bat_path}/voltage_now" ]]; then + local current_ua=$(cat "${bat_path}/current_now" 2>/dev/null || echo 0) + local voltage_uv=$(cat "${bat_path}/voltage_now" 2>/dev/null || echo 0) + power_uw=$(( (current_ua * voltage_uv) / 1000000 )) + [[ $power_uw -gt 0 ]] && break + fi + done + fi + + # Method 3: Try AC power measurement + if [[ $power_uw -eq 0 && -f /sys/class/power_supply/AC/power_now ]]; then + power_uw=$(cat /sys/class/power_supply/AC/power_now 2>/dev/null || echo 0) + fi + + # Method 4: Try other AC power supply names + if [[ $power_uw -eq 0 ]]; then + for ac_path in /sys/class/power_supply/ACAD/* /sys/class/power_supply/ADP*; do + if [[ -f "${ac_path}/power_now" ]]; then + power_uw=$(cat "${ac_path}/power_now" 2>/dev/null || echo 0) + [[ $power_uw -gt 0 ]] && break + fi + done + fi + + # Convert from microwatts to watts and ensure dot decimal separator + if command -v bc &> /dev/null; then + echo "scale=2; $power_uw / 1000000" | bc -l | tr ',' '.' + else + # Fallback to awk if bc is not available + awk "BEGIN {printf \"%.2f\", $power_uw / 1000000}" | tr ',' '.' + fi +} + +get_cpu_freq() { + local total=0 + local count=0 + for freq_file in /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq; do + if [[ -f "$freq_file" ]]; then + local freq=$(cat "$freq_file" 2>/dev/null || echo 0) + total=$((total + freq)) + count=$((count + 1)) + fi + done + if [[ $count -gt 0 ]]; then + echo "scale=0; $total / $count / 1000" | bc -l # Convert to MHz + else + echo "0" + fi +} + +get_temperature() { + local temp=0 + if [[ -f /sys/class/thermal/thermal_zone0/temp ]]; then + temp=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null || echo 0) + echo "scale=1; $temp / 1000" | bc -l | tr ',' '.' # Convert to Celsius and ensure dot + else + echo "0" + fi +} + +benchmark_current_settings() { + log "Starting benchmark for $DURATION seconds..." + log "Current CPU governor: $(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null || echo 'unknown')" + + local power_sum=0 + local freq_sum=0 + local temp_sum=0 + local samples=0 + + for ((i=0; i /dev/null + fi + done + + sleep 2 # Allow governor to settle +} + +test_governors() { + local available_governors + available_governors=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors 2>/dev/null || echo "") + + log "Available governors: $available_governors" + log "Testing each governor for $DURATION seconds..." + + for governor in $available_governors; do + log "Testing governor: $governor" + apply_governor "$governor" + benchmark_current_settings + sleep 5 # Rest between tests + done +} + +show_current_status() { + log "=== Current Power Status ===" + log "CPU Governor: $(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null || echo 'unknown')" + log "Current Power Draw: $(get_power_usage)W" + log "Average CPU Frequency: $(get_cpu_freq)MHz" + log "CPU Temperature: $(get_temperature)°C" + + # Show auto-cpufreq status if available + if command -v auto-cpufreq &> /dev/null; then + log "Auto-cpufreq status:" + sudo auto-cpufreq --stats 2>/dev/null || log "Could not get auto-cpufreq stats" + fi +} + +show_help() { + cat << EOF +Power Benchmarking Script + +Usage: $0 [options] [duration] + +Options: + -s, --status Show current power status + -t, --test-all Test all available CPU governors + -g, --governor GOV Set specific CPU governor + -h, --help Show this help message + +Examples: + $0 30 Benchmark current settings for 30 seconds + $0 --test-all 45 Test all governors for 45 seconds each + $0 --governor powersave Set governor to powersave + $0 --status Show current power status + +Log file will be created at: $LOGFILE +EOF +} + +main() { + # Check if running as root for governor changes + if [[ "${1:-}" == "--governor" ]] && [[ $EUID -ne 0 ]]; then + log "Error: Governor changes require root privileges" + exit 1 + fi + + case "${1:-}" in + -h|--help) + show_help + exit 0 + ;; + -s|--status) + show_current_status + ;; + -t|--test-all) + test_governors + ;; + -g|--governor) + if [[ -z "${2:-}" ]]; then + log "Error: Governor name required" + exit 1 + fi + apply_governor "$2" + show_current_status + ;; + *) + if [[ "${1:-}" =~ ^[0-9]+$ ]]; then + DURATION=$1 + fi + benchmark_current_settings + ;; + esac + + log "Log saved to: $LOGFILE" +} + +# Check dependencies +if ! command -v bc &> /dev/null; then + log "Error: 'bc' calculator is required but not installed" + exit 1 +fi + +main "$@" \ No newline at end of file diff --git a/modules/system/common/hardware/powersave/power-tuning.sh b/modules/system/common/hardware/powersave/power-tuning.sh new file mode 100644 index 0000000..513f526 --- /dev/null +++ b/modules/system/common/hardware/powersave/power-tuning.sh @@ -0,0 +1,325 @@ +#!/usr/bin/env bash + +# Power Tuning Script for ASUS Vivobook Pro 16 +# This script allows runtime adjustment of power settings for testing + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SETTINGS_FILE="/tmp/power-settings-backup.conf" + +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" +} + +backup_current_settings() { + log "Backing up current settings to $SETTINGS_FILE" + + cat > "$SETTINGS_FILE" << EOF +# Power settings backup - $(date) +cpu_governor=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null || echo "unknown") +EOF + + # Backup PCIe ASPM if available + if [[ -f /sys/module/pcie_aspm/parameters/policy ]]; then + echo "pcie_aspm=$(cat /sys/module/pcie_aspm/parameters/policy)" >> "$SETTINGS_FILE" + fi + + # Backup runtime PM settings for some common devices + for device in /sys/bus/pci/devices/*/power/control; do + if [[ -f "$device" ]]; then + local device_path=$(dirname "$device") + local device_id=$(basename "$(dirname "$device_path")") + echo "runtime_pm_${device_id}=$(cat "$device")" >> "$SETTINGS_FILE" + fi + done +} + +restore_settings() { + if [[ ! -f "$SETTINGS_FILE" ]]; then + log "No backup file found at $SETTINGS_FILE" + return 1 + fi + + log "Restoring settings from $SETTINGS_FILE" + + # shellcheck source=/dev/null + source "$SETTINGS_FILE" + + # Restore CPU governor + if [[ -n "${cpu_governor:-}" ]]; then + set_cpu_governor "$cpu_governor" + fi + + log "Settings restored" +} + +set_cpu_governor() { + local governor=$1 + local available_governors + available_governors=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors 2>/dev/null || echo "") + + if [[ ! "$available_governors" =~ $governor ]]; then + log "Error: Governor '$governor' not available. Available: $available_governors" + return 1 + fi + + log "Setting CPU governor to: $governor" + for cpu_dir in /sys/devices/system/cpu/cpu*/cpufreq/; do + if [[ -f "${cpu_dir}scaling_governor" ]]; then + echo "$governor" > "${cpu_dir}scaling_governor" + fi + done +} + +set_cpu_frequency_limits() { + local min_freq=${1:-} + local max_freq=${2:-} + + if [[ -n "$min_freq" ]]; then + log "Setting minimum CPU frequency to: ${min_freq}MHz" + for cpu_dir in /sys/devices/system/cpu/cpu*/cpufreq/; do + if [[ -f "${cpu_dir}scaling_min_freq" ]]; then + echo $((min_freq * 1000)) > "${cpu_dir}scaling_min_freq" + fi + done + fi + + if [[ -n "$max_freq" ]]; then + log "Setting maximum CPU frequency to: ${max_freq}MHz" + for cpu_dir in /sys/devices/system/cpu/cpu*/cpufreq/; do + if [[ -f "${cpu_dir}scaling_max_freq" ]]; then + echo $((max_freq * 1000)) > "${cpu_dir}scaling_max_freq" + fi + done + fi +} + +set_pcie_aspm() { + local policy=$1 + local available_policies + + if [[ ! -f /sys/module/pcie_aspm/parameters/policy ]]; then + log "PCIe ASPM not available" + return 1 + fi + + available_policies=$(cat /sys/module/pcie_aspm/parameters/policy | tr -d '[]') + + if [[ ! "$available_policies" =~ $policy ]]; then + log "Error: PCIe ASPM policy '$policy' not available. Available: $available_policies" + return 1 + fi + + log "Setting PCIe ASPM policy to: $policy" + echo "$policy" > /sys/module/pcie_aspm/parameters/policy +} + +set_runtime_pm() { + local mode=$1 # "auto" or "on" + + log "Setting runtime power management to: $mode" + + # Apply to PCI devices + for device in /sys/bus/pci/devices/*/power/control; do + if [[ -f "$device" ]]; then + echo "$mode" > "$device" + fi + done + + # Apply to USB devices (be careful with input devices) + for device in /sys/bus/usb/devices/*/power/control; do + if [[ -f "$device" ]]; then + local device_path=$(dirname "$device") + local product_file="${device_path}/product" + + # Skip input devices like keyboards and mice + if [[ -f "$product_file" ]]; then + local product=$(cat "$product_file" 2>/dev/null || echo "") + if [[ ! "$product" =~ (Keyboard|Mouse|HID) ]]; then + echo "$mode" > "$device" + fi + else + echo "$mode" > "$device" + fi + fi + done +} + +set_wifi_power_save() { + local mode=$1 # "on" or "off" + + log "Setting WiFi power save to: $mode" + + # Find wireless interfaces + for iface in /sys/class/net/*/wireless; do + if [[ -d "$iface" ]]; then + local interface=$(basename "$(dirname "$iface")") + if command -v iw &> /dev/null; then + iw dev "$interface" set power_save "$mode" 2>/dev/null || log "Could not set power save for $interface" + fi + fi + done +} + +apply_power_profile() { + local profile=$1 + + backup_current_settings + + case "$profile" in + "max-performance") + log "Applying maximum performance profile" + set_cpu_governor "performance" + set_pcie_aspm "performance" 2>/dev/null || true + set_runtime_pm "on" + set_wifi_power_save "off" + ;; + "balanced") + log "Applying balanced profile" + set_cpu_governor "schedutil" + set_pcie_aspm "default" 2>/dev/null || true + set_runtime_pm "auto" + set_wifi_power_save "on" + ;; + "max-powersave") + log "Applying maximum power save profile" + set_cpu_governor "powersave" + set_pcie_aspm "powersupersave" 2>/dev/null || true + set_runtime_pm "auto" + set_wifi_power_save "on" + ;; + "custom-powersave") + log "Applying custom power save profile" + set_cpu_governor "powersave" + set_cpu_frequency_limits "" "2000" # Limit max frequency to 2GHz + set_pcie_aspm "powersupersave" 2>/dev/null || true + set_runtime_pm "auto" + set_wifi_power_save "on" + ;; + *) + log "Unknown profile: $profile" + log "Available profiles: max-performance, balanced, max-powersave, custom-powersave" + return 1 + ;; + esac + + log "Profile '$profile' applied successfully" +} + +run_benchmark() { + local duration=${1:-30} + + if [[ -f "$SCRIPT_DIR/power-benchmark.sh" ]]; then + log "Running benchmark for $duration seconds..." + "$SCRIPT_DIR/power-benchmark.sh" "$duration" + else + log "Benchmark script not found at $SCRIPT_DIR/power-benchmark.sh" + fi +} + +show_current_settings() { + log "=== Current Power Settings ===" + log "CPU Governor: $(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null || echo 'unknown')" + + # Show frequency limits + local min_freq max_freq + min_freq=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq 2>/dev/null || echo 0) + max_freq=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq 2>/dev/null || echo 0) + log "CPU Frequency Range: $((min_freq / 1000))MHz - $((max_freq / 1000))MHz" + + # Show PCIe ASPM + if [[ -f /sys/module/pcie_aspm/parameters/policy ]]; then + log "PCIe ASPM Policy: $(cat /sys/module/pcie_aspm/parameters/policy)" + fi + + # Show runtime PM status for a few devices + local runtime_pm_count=0 + for device in /sys/bus/pci/devices/*/power/control; do + if [[ -f "$device" && $runtime_pm_count -lt 3 ]]; then + local device_id=$(basename "$(dirname "$(dirname "$device")")") + log "Runtime PM ($device_id): $(cat "$device")" + runtime_pm_count=$((runtime_pm_count + 1)) + fi + done +} + +show_help() { + cat << EOF +Power Tuning Script + +Usage: $0 [command] [options] + +Commands: + profile Apply a power profile + governor Set CPU governor + freq-limits Set CPU frequency limits (MHz) + pcie-aspm Set PCIe ASPM policy + runtime-pm Set runtime PM (auto/on) + wifi-powersave Set WiFi power save (on/off) + benchmark [duration] Run power benchmark + backup Backup current settings + restore Restore backed up settings + status Show current settings + help Show this help + +Power Profiles: + max-performance Maximum performance, highest power + balanced Balanced performance and power + max-powersave Maximum power savings + custom-powersave Custom power save with frequency limits + +Examples: + $0 profile max-powersave + $0 governor powersave + $0 freq-limits 800 2000 + $0 benchmark 60 + $0 restore + +Note: Most commands require root privileges +EOF +} + +main() { + if [[ $EUID -ne 0 ]]; then + log "Warning: Most power tuning operations require root privileges" + fi + + case "${1:-help}" in + "profile") + apply_power_profile "${2:-balanced}" + ;; + "governor") + set_cpu_governor "${2:-}" + ;; + "freq-limits") + set_cpu_frequency_limits "${2:-}" "${3:-}" + ;; + "pcie-aspm") + set_pcie_aspm "${2:-}" + ;; + "runtime-pm") + set_runtime_pm "${2:-auto}" + ;; + "wifi-powersave") + set_wifi_power_save "${2:-on}" + ;; + "benchmark") + run_benchmark "${2:-30}" + ;; + "backup") + backup_current_settings + ;; + "restore") + restore_settings + ;; + "status") + show_current_settings + ;; + "help"|*) + show_help + ;; + esac +} + +main "$@" \ No newline at end of file diff --git a/modules/system/common/networks/print/print.nix b/modules/system/common/networks/print/print.nix deleted file mode 100644 index 482840c..0000000 --- a/modules/system/common/networks/print/print.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfg = config.modules.system.networks.print; -in { - options.modules.system.networks.print = { - enable = mkEnableOption "Print services"; - cups.enable = mkOption { - type = types.bool; - default = true; - description = "Enable CUPS (Common Unix Printing System)"; - }; - drivers = mkOption { - type = types.listOf types.package; - default = [ pkgs.cnijfilter2 ]; - description = '' - List of printer drivers to use. Defaults to [ pkgs.cnijfilter2 ], which - is an unfree package for Canon printers. - ''; - }; - }; - - config = mkIf cfg.enable { - services.printing.enable = cfg.cups.enable; - services.printing.drivers = lib.mkIf cfg.cups.enable cfg.drivers; - }; -} \ No newline at end of file diff --git a/modules/system/common/networks/vpn.nix b/modules/system/common/networks/vpn.nix index 5863276..117d824 100644 --- a/modules/system/common/networks/vpn.nix +++ b/modules/system/common/networks/vpn.nix @@ -1,24 +1,33 @@ -{ - pkgs, - lib, - config, - ... -}: +{ lib, config, vars, ... }: + let + inherit (lib) mkEnableOption mkOption types; cfg = config.modules.system.networks; -in -{ +in { options.modules.system.networks = { - vpn = lib.mkOption { - type = lib.types.listOf (lib.types.enum ["tailscale"]); + vpn = mkOption { + type = types.listOf (types.enum [ "tailscale" ]); default = []; description = "List of VPN services to enable. Currently only 'tailscale' is supported."; }; + + disable-ssh = mkEnableOption "Disable SSH access over VPN"; + + disable-op-user = mkEnableOption "Disable operator user for VPN services"; }; - config = { + config = let + tailscaleEnabled = lib.elem "tailscale" cfg.vpn; + tsCfg = { + ssh = !cfg.disable-ssh; + operator = if cfg.disable-op-user then null else vars.user; + }; + in lib.mkIf tailscaleEnabled { services.tailscale = { - enable = lib.elem "tailscale" cfg.vpn; + enable = true; + extraUpFlags = [] + ++ lib.optional tsCfg.ssh "--ssh" + ++ lib.optional (tsCfg.operator != null) "--operator=${tsCfg.operator}"; }; }; -} +} \ No newline at end of file diff --git a/modules/system/common/nix/default.nix b/modules/system/common/nix/default.nix new file mode 100644 index 0000000..ee94679 --- /dev/null +++ b/modules/system/common/nix/default.nix @@ -0,0 +1,56 @@ +{ + lib, + pkgs, + config, + ... +}: + +let + cfg = config.modules.system.nix; +in +{ + options.modules.system.nix = { + rebootTo = lib.mkOption { + type = lib.types.bool; + default = config.boot.loader.grub.enable; + description = "Enable the 'reboot-to' script to select boot entries interactively"; + }; + }; + + config = { + environment.systemPackages = [ + (lib.mkIf cfg.rebootTo (pkgs.writeScriptBin "reboot-to" '' + #!${pkgs.bash}/bin/bash + + if [ "$EUID" -ne 0 ]; then + echo "Please run as root" + exit 1 + fi + + # Get all GRUB entries + entries=$(grep -E "menuentry ['\"].*['\"]" /boot/grub/grub.cfg | sed -E "s/menuentry ['\"](.*?)['\"].*/\1/") + + if [ "$1" = "list" ]; then + echo "$entries" | nl + exit 0 + fi + + # If no argument provided, use fzf to select + if [ -z "$1" ]; then + selected=$(echo "$entries" | ${pkgs.fzf}/bin/fzf --prompt="Select boot entry: ") + else + selected=$1 + fi + + if [ -n "$selected" ]; then + grub-reboot "$selected" + echo "System will reboot to '$selected' on next boot" + echo "Run 'reboot' to restart now" + else + echo "No entry selected" + exit 1 + fi + '')) + ]; + }; +} diff --git a/modules/system/common/nix/nix-garbage.nix b/modules/system/common/nix/nix-garbage.nix index 146cf7d..22aaec5 100644 --- a/modules/system/common/nix/nix-garbage.nix +++ b/modules/system/common/nix/nix-garbage.nix @@ -7,6 +7,12 @@ in options.modules.system.nix.nix-garbage = { enable = lib.mkEnableOption "Enable automatic garbage collection for Nix"; + autoGarbageCollection = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to enable automatic garbage collection"; + }; + dates = lib.mkOption { type = lib.types.str; default = "weekly"; @@ -33,7 +39,7 @@ in }; gc = { - automatic = true; + automatic = lib.mkForce cfg.autoGarbageCollection; dates = cfg.dates; options = "--delete-older-than ${cfg.optionDate}"; }; diff --git a/modules/system/common/nix/polkit.nix b/modules/system/common/nix/polkit.nix index e0c0f19..9c2539c 100644 --- a/modules/system/common/nix/polkit.nix +++ b/modules/system/common/nix/polkit.nix @@ -1,4 +1,4 @@ -{ lib, config, ... }: +{ lib, config, pkgs, ... }: let cfg = config.modules.system.nix.polkit; @@ -18,5 +18,7 @@ in } }); ''; + + environment.systemPackages = with pkgs; [ kdePackages.kdenetwork-filesharing ]; }; } diff --git a/modules/system/common/server/communication/agents.nix b/modules/system/common/server/communication/agents.nix new file mode 100644 index 0000000..98de453 --- /dev/null +++ b/modules/system/common/server/communication/agents.nix @@ -0,0 +1,29 @@ +{ + lib, + config, + ... +}: +let + cfg = config.modules.system.server.communication.agents; + + supportedAgents = [ "qemu" "glpi" ]; +in +{ + options.modules.system.server.communication.agents = lib.mkOption { + type = lib.types.listOf (lib.types.enum supportedAgents); + default = []; + description = '' + List of agents systems to enable. + Available options: ${lib.concatStringsSep ", " supportedAgents} + ''; + }; + + config = { + services = { + qemuGuest.enable = lib.mkIf (lib.elem "qemu" cfg) true; + spice-vdagentd.enable = lib.mkIf (lib.elem "qemu" cfg) true; + glpiAgent.enable = lib.mkIf (lib.elem "glpi" cfg) true; + fusionInventory.enable = lib.mkIf (lib.elem "glpi" cfg) true; + }; + }; +} diff --git a/modules/system/common/server/communication/deskflow.nix b/modules/system/common/server/communication/deskflow.nix new file mode 100644 index 0000000..6edfc3d --- /dev/null +++ b/modules/system/common/server/communication/deskflow.nix @@ -0,0 +1,14 @@ +{ pkgs, lib, config, vars, ... }: + +let + cfg = config.modules.system.server.communication.deskflow; +in +{ + options.modules.system.server.communication.deskflow.enable = lib.mkEnableOption "Enable Deskflow server"; + + config = lib.mkIf cfg.enable { + environment.systemPackages = (with pkgs; [ + deskflow + ]); + }; +} \ No newline at end of file diff --git a/modules/system/common/server/communication/matrix.nix b/modules/system/common/server/communication/matrix.nix new file mode 100644 index 0000000..a7f15fe --- /dev/null +++ b/modules/system/common/server/communication/matrix.nix @@ -0,0 +1,184 @@ +{ config, pkgs, lib, ... }: + +let + cfg = config.modules.system.server.communication.matrix-synapse; +in +{ + options.modules.system.server.communication.matrix-synapse = { + enable = lib.mkEnableOption "Matrix Synapse homeserver"; + + serverName = lib.mkOption { + type = lib.types.str; + default = "${config.networking.hostName}.com"; + example = "example.com"; + description = "The public server name for Matrix federation"; + }; + + publicBaseUrl = lib.mkOption { + type = lib.types.str; + default = "https://matrix.${cfg.serverName}"; + description = "Public base URL for client access"; + }; + + enableRegistration = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to allow new user registration"; + }; + + registrationRequireToken = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether registration requires an access token"; + }; + + databaseType = lib.mkOption { + type = lib.types.enum [ "sqlite" "postgres" ]; + default = "sqlite"; + description = "Database backend to use"; + }; + + postgres = { + host = lib.mkOption { + type = lib.types.str; + default = "/run/postgresql"; + description = "PostgreSQL host (path to socket for local)"; + }; + + database = lib.mkOption { + type = lib.types.str; + default = "matrix-synapse"; + description = "PostgreSQL database name"; + }; + + username = lib.mkOption { + type = lib.types.str; + default = "matrix-synapse"; + description = "PostgreSQL username"; + }; + }; + + enableMetrics = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to enable Prometheus metrics endpoint"; + }; + + logLevel = lib.mkOption { + type = lib.types.enum [ "DEBUG" "INFO" "WARNING" "ERROR" "CRITICAL" ]; + default = "INFO"; + description = "Logging level"; + }; + + extraConfig = lib.mkOption { + type = lib.types.attrs; + default = {}; + description = "Additional configuration options"; + }; + }; + + config = lib.mkIf cfg.enable { + services.matrix-synapse = { + enable = true; + + settings = lib.recursiveUpdate { + server_name = cfg.serverName; + public_baseurl = cfg.publicBaseUrl; + + enable_registration = cfg.enableRegistration; + registration_requires_token = cfg.registrationRequireToken; + + database = if cfg.databaseType == "postgres" then { + name = "psycopg2"; + args = { + user = cfg.postgres.username; + database = cfg.postgres.database; + host = cfg.postgres.host; + }; + } else { + name = "sqlite3"; + args.database = "/var/lib/matrix-synapse/homeserver.db"; + }; + + listeners = [ + { + port = 8008; + bind_addresses = [ "::1" "127.0.0.1" ]; + type = "http"; + tls = false; + x_forwarded = true; + resources = [ + { + names = [ "client" "federation" ]; + compress = false; + } + ]; + } + ] ++ lib.optional cfg.enableMetrics { + port = 9000; + bind_addresses = [ "::1" "127.0.0.1" ]; + type = "metrics"; + tls = false; + }; + + log_config = "/var/lib/matrix-synapse/${cfg.logLevel}.log.config"; + + # Basic rate limiting + rc_messages_per_second = 10; + rc_message_burst_count = 100; + } cfg.extraConfig; + }; + + # PostgreSQL configuration if selected + services.postgresql = lib.mkIf (cfg.databaseType == "postgres") { + enable = true; + ensureDatabases = [ cfg.postgres.database ]; + ensureUsers = [ + { + name = cfg.postgres.username; + ensureDBOwnership = true; + } + ]; + }; + + # Reverse proxy configuration + services.nginx = { + enable = true; + virtualHosts."${cfg.publicBaseUrl}" = { + forceSSL = false; + serverAliases = [ cfg.serverName ]; + + locations."/" = { + proxyPass = "http://[::1]:8008"; + proxyWebsockets = true; + recommendedProxySettings = true; + }; + + locations."/_matrix" = { + proxyPass = "http://[::1]:8008"; + proxyWebsockets = true; + recommendedProxySettings = true; + }; + }; + }; + + # Firewall configuration + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + # Systemd service hardening + systemd.services.matrix-synapse = { + serviceConfig = { + PrivateTmp = true; + ProtectSystem = "strict"; + ProtectHome = true; + PrivateDevices = true; + NoNewPrivileges = true; + }; + }; + + # Integration for matrix + #services = { + #matrix-hookshot.enable = true; + #}; + }; +} \ No newline at end of file diff --git a/modules/system/common/server/communication/ntfy-sh.nix b/modules/system/common/server/communication/ntfy-sh.nix new file mode 100644 index 0000000..0d750f0 --- /dev/null +++ b/modules/system/common/server/communication/ntfy-sh.nix @@ -0,0 +1,48 @@ +{ + pkgs, + lib, + config, + ... +}: +let + cfg = config.modules.system.server.communication.ntfy-sh; +in +{ + options.modules.system.server.communication.ntfy-sh = { + enable = lib.mkEnableOption "Enable ntfy-sh server"; + baseUrl = lib.mkOption { + type = lib.types.str; + default = "http://localhost:2586"; + description = "Base URL for the ntfy-sh server"; + }; + gui.enable = lib.mkEnableOption "Enable ntfy-sh GUI"; + configure-client = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to configure the ntfy client with the server URL"; + }; + }; + + config = { + services.ntfy-sh = { + enable = cfg.enable; + settings = { + base-url = cfg.baseUrl; + # Add other required settings here + listen-http = ":2586"; + }; + }; + + environment.systemPackages = lib.mkIf cfg.gui.enable (with pkgs; [ + notify-client + ]); + + home-manager.sharedModules = lib.mkIf cfg.configure-client [ + { + home.file.".config/ntfy/client.yml".text = '' + default-host: ${cfg.baseUrl} + ''; + } + ]; + }; +} \ No newline at end of file diff --git a/modules/system/common/server/dev/vs-code.nix b/modules/system/common/server/dev/vs-code.nix new file mode 100644 index 0000000..a6897af --- /dev/null +++ b/modules/system/common/server/dev/vs-code.nix @@ -0,0 +1,53 @@ +{ + pkgs, + lib, + config, + vars, + ... +}: +let + cfg = config.modules.system.server.dev.vs-code; +in +{ + options.modules.system.server.dev.vs-code = { + enable = lib.mkEnableOption "Enable VS Code server"; + + port = lib.mkOption { + type = lib.types.port; + default = 3000; + description = "Port for the VS Code server to listen on."; + }; + host = lib.mkOption { + type = lib.types.str; + default = "localhost"; + description = "Host for the VS Code server to bind to."; + }; + extraPackages = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = []; + description = "Additional packages to install for the VS Code server."; + }; + extraEnvironment = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = {}; + description = "Additional environment variables for the VS Code server."; + }; + extraArguments = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + description = "Additional command line arguments for the VS Code server."; + }; + }; + + config = { + services.code-server = { + enable = cfg.enable; + port = cfg.port; + host = cfg.host; + extraPackages = cfg.extraPackages; + extraEnvironment = cfg.extraEnvironment; + extraArguments = cfg.extraArguments; + user = "${vars.user}"; + }; + }; +} diff --git a/modules/system/common/server/games/sunshine.nix b/modules/system/common/server/games/sunshine.nix new file mode 100755 index 0000000..fc7fd95 --- /dev/null +++ b/modules/system/common/server/games/sunshine.nix @@ -0,0 +1,183 @@ +{ config, lib, pkgs, vars, ... }: + +let + cfg = config.modules.system.server.games.sunshine; +in +{ + options.modules.system.server.games.sunshine = { + enable = lib.mkEnableOption "Sunshine streaming server"; + + allowedNetworks = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ + "192.168.0.0/16" + "10.0.0.0/8" + ]; + description = "Networks allowed to connect to Sunshine"; + }; + + settings = lib.mkOption { + type = lib.types.attrs; + default = { + # key_rightalt_to_key_win = "enabled"; + adapter_name = "/dev/dri/renderD128"; + }; + description = "Sunshine configuration settings"; + }; + + applications = lib.mkOption { + type = lib.types.listOf (lib.types.submodule { + options = { + name = lib.mkOption { + type = lib.types.str; + description = "Name of the application profile"; + }; + output = lib.mkOption { + type = lib.types.str; + description = "Output display for this profile"; + }; + autoDetach = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to auto-detach after session ends"; + }; + prepCommands = lib.mkOption { + type = lib.types.submodule { + options = { + do = lib.mkOption { + type = lib.types.str; + description = "Command to run when starting stream"; + }; + undo = lib.mkOption { + type = lib.types.str; + description = "Command to run when ending stream"; + }; + }; + }; + description = "Preparation commands for this profile"; + }; + }; + }); + default = [ + { + name = "Programming Mode (Note 11 2400*1080)"; + output = "DP-5"; + autoDetach = true; + prepCommands = { + do = '' + sed -i 's/\$mainMod = Super/\$mainMod = ALT_R/' ~/.config/hypr/keybindings.conf + ${pkgs.hyprland}/bin/hyprctl keyword input:kb_layout "us" + ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-4,disable + ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-6,disable + ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-5,addreserved,0,0,0,0 + ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-5,2320x1080@60,0x0,2 + ${pkgs.hyprland}/bin/hyprctl keyword misc:cursor_zoom_factor 2 + ${pkgs.hyprland}/bin/hyprctl keyword misc:no_direct_scanout 1 + ''; + undo = '' + sed -i 's/\$mainMod = ALT_R/\$mainMod = Super/' ~/.config/hypr/keybindings.conf + Hyde reload + ${pkgs.hyprland}/bin/hyprctl reload + ''; + }; + } + { + name = "Mobile Stream (1080p)"; + output = "DP-5"; + autoDetach = true; + prepCommands = { + do = '' + ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-4,disable + ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-6,disable + sleep 1 + ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-5,2560x1440@144,0x0,1.6 + ${pkgs.hyprland}/bin/hyprctl keyword misc:cursor_zoom_factor 1.6 + ''; + undo = '' + ${pkgs.hyprland}/bin/hyprctl reload + ''; + }; + } + { + name = "Mobile Stream (Performance)"; + output = "DP-5"; + autoDetach = true; + prepCommands = { + do = '' + ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-4,disable + ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-6,disable + sleep 1 + ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-5,1920x1080@144,0x0,1.25 + ${pkgs.hyprland}/bin/hyprctl keyword misc:cursor_zoom_factor 1.25 + ''; + undo = '' + ${pkgs.hyprland}/bin/hyprctl reload + ''; + }; + } + ]; + description = "Application profiles for Sunshine streaming"; + }; + + environment = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = { + PATH = "$(PATH):$(HOME)/.local/bin"; + }; + description = "Environment variables for Sunshine applications"; + }; + + openFirewall = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to open firewall ports for Sunshine"; + }; + + autoStart = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to start Sunshine automatically"; + }; + + capSysAdmin = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to grant CAP_SYS_ADMIN capability to Sunshine"; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = with pkgs; [ sunshine ]; + + services.sunshine = { + enable = true; + inherit (cfg) autoStart capSysAdmin openFirewall; + settings = cfg.settings; + applications = { + env = cfg.environment; + apps = map (app: { + name = app.name; + prep-cmd = [{ + do = pkgs.writeShellScript "${lib.strings.sanitizeDerivationName app.name}-do" app.prepCommands.do; + undo = pkgs.writeShellScript "${lib.strings.sanitizeDerivationName app.name}-undo" app.prepCommands.undo; + }]; + auto-detach = lib.boolToString app.autoDetach; + output = app.output; + }) cfg.applications; + }; + }; + + users.groups.keyd.members = [ vars.user ]; + users.users.${vars.user}.extraGroups = [ "video" "input" "render" "kvm" "uinput" ]; + + boot.kernelModules = [ "uinput" ]; + services.udev.extraRules = '' + KERNEL=="uinput", SUBSYSTEM=="misc", MODE="0660", GROUP="input" + ''; + + networking.firewall.extraCommands = lib.concatMapStrings (net: '' + iptables -A INPUT -p tcp -s ${net} --dport 47984:47990 -j ACCEPT + iptables -A INPUT -p udp -s ${net} --dport 47998:48000 -j ACCEPT + '') cfg.allowedNetworks; + }; +} \ No newline at end of file diff --git a/modules/system/common/server/meal.nix b/modules/system/common/server/meal.nix new file mode 100644 index 0000000..d48ba40 --- /dev/null +++ b/modules/system/common/server/meal.nix @@ -0,0 +1,37 @@ +{ + pkgs, + lib, + config, + ... +}: +let + cfg = config.modules.system.server.meal; +in +{ + options.modules.system.server.meal = { + clients = lib.mkOption { + type = lib.types.listOf (lib.types.enum ["tandoor" "mealie"]); + default = []; + description = "List of meal clients to enable"; + }; + extraConfig = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = {}; + description = "Additional configuration options"; + }; + }; + + config = { + services = { + tandoor-recipes = { + enable = lib.elem "tandoor" cfg.clients; + extraConfig = cfg.extraConfig; + }; + + mealie = { + enable = lib.elem "mealie" cfg.clients; + settings = cfg.extraConfig; + }; + }; + }; +} diff --git a/modules/system/common/server/media/multimedia.nix b/modules/system/common/server/media/multimedia.nix new file mode 100755 index 0000000..fe7918b --- /dev/null +++ b/modules/system/common/server/media/multimedia.nix @@ -0,0 +1,263 @@ +{ pkgs, config, lib, vars, ... }: + +let + cfg = config.modules.system.server.media.multimedia; + mediaUserGroup = "mediaServer"; +in { + options.modules.system.server.media.multimedia = { + enable = lib.mkEnableOption "Enable the complete media server stack"; + + user = lib.mkOption { + type = lib.types.str; + default = mediaUserGroup; + description = "User to run all media services as"; + }; + group = lib.mkOption { + type = lib.types.str; + default = mediaUserGroup; + description = "Group to run all media services as"; + }; + + dataDir = lib.mkOption { + type = lib.types.str; + default = "/var/lib/${mediaUserGroup}"; + description = "Base directory for service data"; + }; + + # Plex options + plex = { + enable = lib.mkEnableOption "Enable Plex Media Server"; + openFirewall = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Open firewall ports for Plex"; + }; + }; + + # Jellyfin options + jellyfin = { + enable = lib.mkEnableOption "Enable Jellyfin Media Server"; + openFirewall = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Open firewall ports for Jellyfin"; + }; + }; + + # Sonarr options + sonarr = { + enable = lib.mkEnableOption "Enable Sonarr"; + openFirewall = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Open firewall ports for Sonarr"; + }; + }; + + # Radarr options + radarr = { + enable = lib.mkEnableOption "Enable Radarr"; + openFirewall = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Open firewall ports for Radarr"; + }; + }; + + # Prowlarr options + prowlarr = { + enable = lib.mkEnableOption "Enable Prowlarr"; + openFirewall = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Open firewall ports for Prowlarr"; + }; + }; + + # Bazarr options + bazarr = { + enable = lib.mkEnableOption "Enable Bazarr"; + openFirewall = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Open firewall ports for Bazarr"; + }; + listenPort = lib.mkOption { + type = lib.types.port; + default = 6767; + description = "Port for Bazarr to listen on"; + }; + }; + + # Flaresolverr options + flaresolverr = { + enable = lib.mkEnableOption "Enable Flaresolverr"; + openFirewall = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Open firewall ports for Flaresolverr"; + }; + port = lib.mkOption { + type = lib.types.port; + default = 8191; + description = "Port for Flaresolverr to listen on"; + }; + }; + + # Tautulli options + tautulli = { + enable = lib.mkEnableOption "Enable Tautulli"; + openFirewall = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Open firewall ports for Tautulli"; + }; + port = lib.mkOption { + type = lib.types.port; + default = 8181; + description = "Port for Tautulli to listen on"; + }; + }; + + # jellyseerr options + jellyseerr = { + enable = lib.mkEnableOption "Enable jellyseerr"; + openFirewall = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Open firewall ports for jellyseerr"; + }; + port = lib.mkOption { + type = lib.types.port; + default = 5055; + description = "Port for jellyseerr to listen on"; + }; + }; + + # qBittorrent options + qbittorrent.enable = lib.mkEnableOption "Enable qBittorrent"; + + # Shared storage options + storage = { + mediaDir = lib.mkOption { + type = lib.types.str; + default = "/data/${mediaUserGroup}"; + description = "Directory for media storage"; + }; + permissions = lib.mkOption { + type = lib.types.str; + default = "0775"; + description = "Permissions for media directories"; + }; + }; + }; + + config = lib.mkIf cfg.enable { + # Create media group + users.groups.${cfg.group} = { + name = cfg.group; + members = [ vars.user ]; + }; + + # Create media user + users.users.${cfg.user} = { + group = cfg.group; + home = cfg.dataDir; + createHome = true; + isSystemUser = true; + }; + + # Create required directories with proper permissions + systemd.tmpfiles.rules = [ + "d ${cfg.dataDir} ${cfg.storage.permissions} ${cfg.user} ${cfg.group}" + "Z ${cfg.dataDir} - ${cfg.user} ${cfg.group}" + "d ${cfg.storage.mediaDir} ${cfg.storage.permissions} ${cfg.user} ${cfg.group}" + "Z ${cfg.storage.mediaDir} - ${cfg.user} ${cfg.group}" + ]; + + # Configure services using the global user/group/dataDir + services = { + plex = lib.mkIf cfg.plex.enable { + enable = true; + openFirewall = cfg.plex.openFirewall; + dataDir = "${cfg.dataDir}/plex"; + user = cfg.user; + group = cfg.group; + }; + + jellyfin = lib.mkIf cfg.jellyfin.enable { + enable = true; + openFirewall = cfg.jellyfin.openFirewall; + dataDir = "${cfg.dataDir}/jellyfin"; + user = cfg.user; + group = cfg.group; + }; + + sonarr = lib.mkIf cfg.sonarr.enable { + enable = true; + openFirewall = cfg.sonarr.openFirewall; + dataDir = "${cfg.dataDir}/sonarr"; + user = cfg.user; + group = cfg.group; + }; + + radarr = lib.mkIf cfg.radarr.enable { + enable = true; + openFirewall = cfg.radarr.openFirewall; + dataDir = "${cfg.dataDir}/radarr"; + user = cfg.user; + group = cfg.group; + }; + + prowlarr = lib.mkIf cfg.prowlarr.enable { + enable = true; + openFirewall = cfg.prowlarr.openFirewall; + }; + + bazarr = lib.mkIf cfg.bazarr.enable { + enable = true; + openFirewall = cfg.bazarr.openFirewall; + user = cfg.user; + group = cfg.group; + listenPort = cfg.bazarr.listenPort; + }; + + flaresolverr = lib.mkIf cfg.flaresolverr.enable { + enable = true; + openFirewall = cfg.flaresolverr.openFirewall; + port = cfg.flaresolverr.port; + }; + + tautulli = lib.mkIf cfg.tautulli.enable { + enable = true; + openFirewall = cfg.tautulli.openFirewall; + dataDir = "${cfg.dataDir}/tautulli"; + user = cfg.user; + group = cfg.group; + port = cfg.tautulli.port; + }; + + jellyseerr = lib.mkIf cfg.jellyseerr.enable { + enable = true; + openFirewall = cfg.jellyseerr.openFirewall; + port = cfg.jellyseerr.port; + }; + }; + + # qBittorrent service + systemd.services.qbittorrent = lib.mkIf cfg.qbittorrent.enable { + description = "qBittorrent"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "simple"; + User = cfg.user; + Group = cfg.group; + ExecStart = "${pkgs.qbittorrent-enhanced-nox}/bin/qbittorrent-nox"; + WorkingDirectory = "${cfg.dataDir}/qbittorrent"; + Restart = "on-failure"; + }; + }; + }; +} \ No newline at end of file diff --git a/modules/system/common/server/media/paperless.nix b/modules/system/common/server/media/paperless.nix new file mode 100644 index 0000000..1d559e5 --- /dev/null +++ b/modules/system/common/server/media/paperless.nix @@ -0,0 +1,81 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.modules.system.server.media.paperless; +in { + options.modules.system.server.media.paperless = { + enable = mkEnableOption "Paperless document management system"; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/paperless"; + description = "Directory where Paperless stores its data."; + }; + + mediaDir = mkOption { + type = types.path; + default = "${cfg.dataDir}/media"; + description = "Directory where Paperless stores documents."; + }; + + consumptionDir = mkOption { + type = types.nullOr types.path; + default = "${cfg.dataDir}/consume"; + description = "Directory where Paperless monitors for new documents."; + }; + + consumptionDirIsPublic = mkOption { + type = types.bool; + default = true; + description = "Whether the consumption directory should be world-readable."; + }; + + port = mkOption { + type = types.port; + default = 28981; + description = "Port to listen on."; + }; + + settings = mkOption { + type = types.attrsOf types.unspecified; + default = {}; + description = "Paperless configuration settings."; + }; + + exporter = { + enable = mkEnableOption "Paperless document exporter"; + + directory = mkOption { + type = types.path; + default = "${cfg.dataDir}/export"; + description = "Directory where exports are stored."; + }; + + onCalendar = mkOption { + type = types.nullOr types.str; + default = null; + description = "Systemd calendar expression for when to run exports."; + }; + }; + }; + + config = { + services.paperless = { + enable = cfg.enable; + dataDir = cfg.dataDir; + mediaDir = cfg.mediaDir; + consumptionDir = cfg.consumptionDir; + consumptionDirIsPublic = cfg.consumptionDirIsPublic; + port = cfg.port; + settings = cfg.settings; + + exporter = { + enable = cfg.exporter.enable; + directory = cfg.exporter.directory; + onCalendar = cfg.exporter.onCalendar; + }; + }; + }; +} \ No newline at end of file diff --git a/modules/system/common/server/media/photoprism.nix b/modules/system/common/server/media/photoprism.nix new file mode 100644 index 0000000..3013153 --- /dev/null +++ b/modules/system/common/server/media/photoprism.nix @@ -0,0 +1,44 @@ +{ + pkgs, + lib, + config, + ... +}: +let + cfg = config.modules.system.server.media.photoprism; +in +{ + options.modules.system.server.media.photoprism = { + enable = lib.mkEnableOption "Enable photoprism server"; + storagePath = lib.mkOption { + type = lib.types.str; + default = "/var/lib/photoprism"; + description = "Base directory for photoprism data"; + }; + originalsPath = lib.mkOption { + type = lib.types.str; + default = "${cfg.storagePath}/originals"; + description = "Path to your original photos directory"; + }; + settings = lib.mkOption { + type = lib.types.attrsOf lib.types.anything; + default = {}; + description = "Custom settings for photoprism configuration"; + }; + port = lib.mkOption { + type = lib.types.port; + default = 2342; + description = "Port for the photoprism web interface"; + }; + }; + + config = lib.mkIf cfg.enable { + services.photoprism = { + enable = true; + port = cfg.port; + originalsPath = cfg.originalsPath; + storagePath = cfg.storagePath; + settings = cfg.settings; + }; + }; +} \ No newline at end of file diff --git a/modules/system/common/server/password-manager/vaultwarden.nix b/modules/system/common/server/password-manager/vaultwarden.nix new file mode 100644 index 0000000..257e597 --- /dev/null +++ b/modules/system/common/server/password-manager/vaultwarden.nix @@ -0,0 +1,32 @@ +{ + pkgs, + lib, + config, + ... +}: +let + cfg = config.modules.system.server.security.password-manager.vaultwarden; +in +{ + options.modules.system.server.security.password-manager.vaultwarden = { + enable = lib.mkEnableOption "Enable Vaultwarden server"; + backupDir = lib.mkOption { + type = lib.types.str; + default = "/var/backup/vaultwarden"; + description = "Base directory for Vaultwarden data"; + }; + settings = lib.mkOption { + type = lib.types.attrsOf lib.types.anything; + default = {}; + description = "Custom settings for photoprism configuration"; + }; + }; + + config = { + services.vaultwarden = { + enable = cfg.enable; + backupDir = cfg.backupDir; + config = cfg.settings; + }; + }; +} diff --git a/modules/system/common/server/print.nix b/modules/system/common/server/print.nix new file mode 100644 index 0000000..e50abab --- /dev/null +++ b/modules/system/common/server/print.nix @@ -0,0 +1,91 @@ +{ config, lib, pkgs, vars, ... }: + +with lib; + +let + cfg = config.modules.system.server.print; +in { + options.modules.system.server.print = { + enable = mkEnableOption "Enable CUPS (Common Unix Printing System)"; + browsed.enable = mkEnableOption "Enable CUPS Browsed for automatic printer discovery"; + + drivers = mkOption { + type = types.listOf types.package; + default = [ pkgs.cnijfilter2 ]; + description = '' + List of printer drivers to use. Defaults to [ pkgs.cnijfilter2 ], which + is an unfree package for Canon printers. + ''; + }; + listenAddresses = mkOption { + type = types.listOf types.str; + default = [ "localhost:631" ]; + description = '' + List of addresses to listen on for incoming print jobs. Defaults to + [ "localhost:631" ]. + ''; + }; + allowFrom = mkOption { + type = types.listOf types.str; + default = [ "localhost" ]; + description = '' + List of addresses allowed to send print jobs. Defaults to [ "localhost" ]. + You can add more addresses or networks as needed. + ''; + }; + logLevel = mkOption { + type = types.str; + default = "info"; + description = '' + Log level for CUPS. Can be set to "debug", "info", "warn", or "error". + Defaults to "info". + ''; + }; + openFirewall = mkOption { + type = types.bool; + default = true; + description = '' + Whether to open the firewall for CUPS. If enabled, it will allow incoming + connections on port 631 (the default CUPS port). + ''; + }; + webInterface = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable the CUPS web interface. If enabled, you can access it + at http://localhost:631. + ''; + }; + gui = { + enable = mkEnableOption "Enable GUI tools for managing printers"; + + packages = mkOption { + type = types.listOf types.package; + default = [ pkgs.system-config-printer ]; + description = '' + List of GUI tools for managing printers. Defaults to + [ pkgs.system-config-printer ]. + ''; + }; + }; + }; + + config = { + services.printing = { + enable = cfg.enable; + drivers = lib.mkIf cfg.enable cfg.drivers; + listenAddresses = cfg.listenAddresses; + allowFrom = cfg.allowFrom; + logLevel = cfg.logLevel; + openFirewall = cfg.openFirewall; + webInterface = cfg.webInterface; + }; + + services.avahi.enable = cfg.browsed.enable; + + users.users.${vars.user}.extraGroups = [ "lp" "lpadmin" ]; + + environment.systemPackages = lib.mkIf cfg.gui.enable cfg.gui.packages; + }; +} \ No newline at end of file diff --git a/modules/system/common/server/storage/gitea.nix b/modules/system/common/server/storage/gitea.nix new file mode 100644 index 0000000..34950e7 --- /dev/null +++ b/modules/system/common/server/storage/gitea.nix @@ -0,0 +1,39 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.modules.system.server.storage.gitea; +in +{ + options.modules.system.server.storage.gitea = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable Gitea service"; + }; + + httpPort = mkOption { + type = types.port; + default = 3000; + description = "HTTP port for Gitea"; + }; + + stateDir = mkOption { + type = types.path; + default = "/var/lib/gitea"; + description = "Gitea state directory"; + }; + }; + + config = mkIf cfg.enable { + services.gitea = { + enable = true; + stateDir = cfg.stateDir; + + settings.server = { + HTTP_PORT = cfg.httpPort; + }; + }; + }; +} \ No newline at end of file diff --git a/modules/system/common/backup/syncthing.nix b/modules/system/common/server/storage/syncthing.nix similarity index 91% rename from modules/system/common/backup/syncthing.nix rename to modules/system/common/server/storage/syncthing.nix index 6772a43..0032c05 100644 --- a/modules/system/common/backup/syncthing.nix +++ b/modules/system/common/server/storage/syncthing.nix @@ -7,10 +7,10 @@ }: let - cfg = config.modules.system.backup.syncthing; + cfg = config.modules.system.server.storage.syncthing; in { - options.modules.system.backup.syncthing = { + options.modules.system.server.storage.syncthing = { enable = lib.mkEnableOption "Enable Syncthing for file synchronization"; port = lib.mkOption { diff --git a/modules/system/common/virtualisation/containers.nix b/modules/system/common/virtualisation/containers.nix deleted file mode 100644 index 4fe9031..0000000 --- a/modules/system/common/virtualisation/containers.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ - pkgs, - config, - lib, - vars, - ... -}: - -let - cfg = config.modules.system.virtualisation.containers; -in -{ - options.modules.system.virtualisation.containers = { - engines = lib.mkOption { - type = lib.types.listOf (lib.types.enum ["docker" "podman"]); - default = []; - description = "List of container engines to enable"; - }; - }; - config = lib.mkMerge [ - (lib.mkIf (lib.elem "docker" cfg.engines) { - users.groups.docker.members = [ "${vars.user}" ]; - - virtualisation.docker.enable = true; - - environment.systemPackages = [ pkgs.docker pkgs.docker-compose ]; - }) - (lib.mkIf (lib.elem "podman" cfg.engines) { - virtualisation.podman.enable = true; - - environment.systemPackages = [ pkgs.podman pkgs.podman-compose ]; - }) - ]; -} diff --git a/modules/system/common/virtualisation/default.nix b/modules/system/common/virtualisation/default.nix new file mode 100644 index 0000000..09c759f --- /dev/null +++ b/modules/system/common/virtualisation/default.nix @@ -0,0 +1,153 @@ +{ config, lib, pkgs, vars, ... }: + +let + inherit (lib) mkEnableOption mkOption types; + + cfg = config.modules.system.virtualisation; + + # List of supported virtualization engines + supportedEngines = [ "libvirtd" "podman" "docker" "waydroid" "lxd" ]; + + # List of supported GUI clients + supportedClients = [ "virt-manager" "droidcam" "quickemu" ]; + + # Map emulators to their packages + emulatorToPackage = with pkgs; { + playonlinux = [ playonlinux ]; + bottles = [ bottles ]; + dosbox = [ dosbox ]; + qemu = [ qemu ]; + virtualbox = [ virtualbox ]; + }; + + # Wine packages based on version + winePackages = with pkgs; { + stable = wine-stable; + staging = wine-staging; + wayland = wineWayland; + fonts = winePackages.stable; + }; + + # Proton packages + protonPackages = with pkgs; [ + protonup-qt + protontricks + proton-caller + ]; + + # Get packages for enabled emulators + baseEmulatorPackages = lib.concatMap (emulator: emulatorToPackage.${emulator} or []) cfg.emulators; + + # Additional packages based on configuration + additionalPackages = with pkgs; [] + ++ lib.optionals cfg.wine.enable [ (winePackages.${cfg.wine.version} or pkgs.wine-stable) winetricks ] + ++ lib.optionals cfg.proton.enable protonPackages + ++ lib.optionals (lib.elem "podman" cfg.engines) [ podman-compose ] + ++ lib.optionals (lib.elem "docker" cfg.engines) [ docker-compose ]; +in { + options.modules.system.virtualisation = { + enable = mkEnableOption "system virtualization support"; + + engines = mkOption { + type = types.listOf (types.enum supportedEngines); + default = []; + description = "List of virtualization engines to enable"; + }; + + gui.clients = mkOption { + type = types.listOf (types.enum supportedClients); + default = []; + description = "List of virtualization GUI clients to install"; + }; + + emulators = mkOption { + type = types.listOf (types.enum (builtins.attrNames emulatorToPackage)); + default = []; + description = "List of emulators to install"; + }; + + wine = { + enable = mkEnableOption "Wine compatibility layer"; + version = mkOption { + type = types.enum (builtins.attrNames winePackages); + default = "stable"; + description = "Wine version to use"; + }; + }; + + proton = { + enable = mkEnableOption "Proton compatibility layer"; + }; + + spiceUSBRedirection = { + enable = mkEnableOption "SPICE USB redirection"; + }; + }; + + config = lib.mkMerge [ + (lib.mkIf cfg.enable { + # Common virtualization configuration + virtualisation = { + spiceUSBRedirection.enable = cfg.spiceUSBRedirection.enable; + }; + + users.users.${vars.user}.extraGroups = ["kvm" "input"] + ++ lib.optionals (lib.elem "libvirtd" cfg.engines) ["libvirtd"] + ++ lib.optionals (lib.elem "docker" cfg.engines) ["docker"] + ++ lib.optionals (lib.elem "podman" cfg.engines) ["podman"] + ++ lib.optionals (lib.elem "lxd" cfg.engines) ["lxd"]; + }) + + (lib.mkIf (lib.elem "libvirtd" cfg.engines) { + virtualisation.libvirtd = { + enable = true; + qemu = { + runAsRoot = true; + swtpm.enable = true; + ovmf.enable = true; + }; + }; + }) + + (lib.mkIf (lib.elem "podman" cfg.engines) { + virtualisation.podman = { + enable = true; + dockerCompat = true; + defaultNetwork.settings.dns_enabled = true; + }; + }) + + (lib.mkIf (lib.elem "docker" cfg.engines) { + virtualisation.docker = { + enable = true; + autoPrune.enable = true; + enableOnBoot = lib.mkDefault false; + }; + }) + + (lib.mkIf (lib.elem "lxd" cfg.engines) { + virtualisation.lxd.enable = true; + }) + + (lib.mkIf (lib.elem "waydroid" cfg.engines) { + virtualisation.waydroid.enable = true; + environment.systemPackages = [ pkgs.waydroid ]; + }) + + (lib.mkIf (lib.elem "virt-manager" cfg.gui.clients) { + programs.virt-manager.enable = true; + }) + + (lib.mkIf (lib.elem "droidcam" cfg.gui.clients) { + environment.systemPackages = [ pkgs.droidcam ]; + }) + + (lib.mkIf (cfg.emulators != []) { + environment.systemPackages = baseEmulatorPackages; + }) + + (lib.mkIf (cfg.wine.enable || cfg.proton.enable) { + environment.systemPackages = additionalPackages; + }) + ]; +} \ No newline at end of file diff --git a/modules/system/common/virtualisation/ollama.nix b/modules/system/common/virtualisation/ollama.nix new file mode 100644 index 0000000..c9891db --- /dev/null +++ b/modules/system/common/virtualisation/ollama.nix @@ -0,0 +1,43 @@ +{ config, lib, pkgs, vars, ... }: + +let + inherit (lib) mkEnableOption mkOption types; + + cfg = config.modules.system.virtualisation.ollama; +in { + options.modules.system.virtualisation.ollama = { + enable = mkEnableOption "Enable Ollama (AI model server)"; + path = mkOption { + type = types.str; + default = "/var/lib/ollama"; + description = "Path to Ollama data directory"; + }; + settings = mkOption { + type = types.attrsOf types.str; + default = {}; + description = "Additional environment variables for Ollama service"; + }; + port = mkOption { + type = types.int; + default = 11434; + description = "Port for Ollama service"; + }; + + gui.enable = mkEnableOption "Enable Ollama LLM UI (Next.js frontend)"; + }; + + config = { + services = { + ollama = { + enable = cfg.enable; + home = cfg.path; + environmentVariables = cfg.settings; + port = cfg.port; + }; + nextjs-ollama-llm-ui = { + enable = cfg.gui.enable; + ollamaUrl = "http://127.0.0.1:${toString cfg.port}"; + }; + }; + }; +} \ No newline at end of file diff --git a/modules/system/hosts/cedar/default.nix b/modules/system/hosts/cedar/default.nix new file mode 100644 index 0000000..e7ac4b5 --- /dev/null +++ b/modules/system/hosts/cedar/default.nix @@ -0,0 +1,52 @@ +{ vars, ... }: +{ + imports = [ + ../../common + ../../../wrapper/safing/module.nix + ]; + + modules.system = { + nix = { + nix-garbage = { + enable = true; + autoOptimiseStore = true; + }; + }; + networks.vpn = ["tailscale"]; + dev.languages.php.enable = true; + server = { + storage = { + syncthing = { + enable = true; + dirSync = "/home/${vars.user}"; + subDir = "Documents"; + }; + }; + dev.vs-code.enable = true; + media = { + multimedia = { + enable = true; + jellyfin.enable = true; + sonarr.enable = true; + radarr.enable = true; + prowlarr.enable = true; + bazarr.enable = true; + jellyseerr.enable = true; + }; + paperless.enable = true; + photoprism.enable = true; + }; + communication = { + ntfy-sh.enable = true; + matrix-synapse.enable = true; + agents = ["qemu"]; + }; + meal.clients = ["mealie"]; + }; + }; + + services.portmaster = { + enable = true; + devmode.enable = true; + }; +} diff --git a/modules/system/hosts/fern/default.nix b/modules/system/hosts/fern/default.nix index fba44d2..a8cda2e 100644 --- a/modules/system/hosts/fern/default.nix +++ b/modules/system/hosts/fern/default.nix @@ -2,9 +2,6 @@ { imports = [ ../../common - ./plex.nix - ./sunshine.nix - ./vfio ]; modules.system = { @@ -20,12 +17,5 @@ interface = "enp7s0"; }; }; - - # fern specific modules - fern = { - plex.enable = true; - sunshine.enable = true; - vfio.enable = true; - }; }; } diff --git a/modules/system/hosts/fern/plex.nix b/modules/system/hosts/fern/plex.nix deleted file mode 100755 index 4b7ea90..0000000 --- a/modules/system/hosts/fern/plex.nix +++ /dev/null @@ -1,84 +0,0 @@ -{ - pkgs, - config, - lib, - ... -}: - -# TODO: proxy, requires passwords -# TODO: sonarr search, requires passwords -# TODO: qbitorrent web instance, requires passwords - -let - cfg = config.modules.system.fern.plex; -in -{ - options.modules.system.fern.plex = { - enable = lib.mkEnableOption "plex"; - }; - - config = lib.mkIf cfg.enable { - services.plex = { - enable = true; - openFirewall = true; - user = "plex"; - group = "plex"; - dataDir = "/var/lib/plex"; - accelerationDevices = [ - "*" - ]; - }; - - fileSystems."/data/plex" = { - device = "/dev/disk/by-id/764A73A84A736431"; - fsType = "ntfs"; - options = [ - "rw" - "uid=plex" - "gid=plex" - "permissions" - "acl" - "dmask=0002" - "fmask=0002" - "nofail" - ]; - }; - - users.groups.plex = { - name = "plex"; - members = [ "richen" ]; - }; - users.users.plex = { - group = "plex"; - home = "/var/lib/plex"; - createHome = true; - isSystemUser = true; - }; - - systemd.tmpfiles.rules = [ - "d /var/lib/plex 0775 plex plex" - "Z /var/lib/plex - plex plex" - "d /data/plex 0775 plex plex" - "Z /data/plex - plex plex" - ]; - - # Add qBittorrent service configuration - systemd.services.qbittorrent = { - description = "qBittorrent"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - - serviceConfig = { - Type = "simple"; - User = "richen"; - Group = "plex"; - ExecStart = "${pkgs.qbittorrent-enhanced-nox}/bin/qbittorrent-nox"; - Restart = "on-failure"; - }; - }; - - environment.systemPackages = with pkgs; [ - plex-desktop - ]; - }; -} diff --git a/modules/system/hosts/fern/sunshine.nix b/modules/system/hosts/fern/sunshine.nix deleted file mode 100755 index 701bc6e..0000000 --- a/modules/system/hosts/fern/sunshine.nix +++ /dev/null @@ -1,137 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: -let - cfg = config.modules.system.fern.sunshine; -in -{ - options.modules.system.fern.sunshine = { - enable = lib.mkEnableOption "Sunshine streaming server"; - - allowedNetworks = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ - "192.168.0.0/16" - "10.0.0.0/8" - ]; - description = "Networks allowed to connect to Sunshine"; - }; - }; - - config = lib.mkIf cfg.enable { - # Install sunshine and required X11 packages - environment.systemPackages = with pkgs; [ - sunshine - ]; - - services.sunshine = { - enable = true; - autoStart = true; - capSysAdmin = true; - openFirewall = true; - settings = { - # key_rightalt_to_key_win = "enabled"; - adapter_name = "/dev/dri/renderD128"; - }; - applications = { - env = { - PATH = "$(PATH):$(HOME)/.local/bin"; - }; - apps = [ - { - name = "Programming Mode (Note 11 2400*1080)"; - prep-cmd = [ - { - do = "${pkgs.writeShellScript "stream-mode" '' - sed -i 's/\$mainMod = Super/\$mainMod = ALT_R/' ~/.config/hypr/keybindings.conf - ${pkgs.hyprland}/bin/hyprctl keyword input:kb_layout "us" - ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-4,disable - ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-6,disable - ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-5,addreserved,0,0,0,0 - ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-5,2320x1080@60,0x0,2 - ${pkgs.hyprland}/bin/hyprctl keyword misc:cursor_zoom_factor 2 - ${pkgs.hyprland}/bin/hyprctl keyword misc:no_direct_scanout 1 - ''}"; - undo = "${pkgs.writeShellScript "regular-mode" '' - sed -i 's/\$mainMod = ALT_R/\$mainMod = Super/' ~/.config/hypr/keybindings.conf - Hyde reload - ${pkgs.hyprland}/bin/hyprctl reload - ''}"; - } - ]; - auto-detach = "true"; - output = "DP-5"; - } - { - name = "Mobile Stream (1080p)"; - prep-cmd = [ - { - do = "${pkgs.writeShellScript "stream-mode" '' - ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-4,disable - ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-6,disable - sleep 1 - ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-5,2560x1440@144,0x0,1.6 - ${pkgs.hyprland}/bin/hyprctl keyword misc:cursor_zoom_factor 1.6 - ''}"; - undo = "${pkgs.writeShellScript "regular-mode" '' - - ${pkgs.hyprland}/bin/hyprctl reload - ''}"; - } - ]; - auto-detach = "true"; - output = "DP-5"; - } - { - name = "Mobile Stream (Performance)"; - prep-cmd = [ - { - do = "${pkgs.writeShellScript "stream-mode" '' - ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-4,disable - ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-6,disable - sleep 1 - ${pkgs.hyprland}/bin/hyprctl keyword monitor DP-5,1920x1080@144,0x0,1.25 - ${pkgs.hyprland}/bin/hyprctl keyword misc:cursor_zoom_factor 1.25 - ''}"; - undo = "${pkgs.writeShellScript "regular-mode" '' - ${pkgs.hyprland}/bin/hyprctl reload - ''}"; - } - ]; - auto-detach = "true"; - output = "DP-5"; - } - ]; - }; - }; - - users.groups.keyd = { - members = [ "richen" ]; - }; - # Additional groups for Wayland/KMS access - users.users.richen.extraGroups = [ - "video" - "input" - "render" - "kvm" - "uinput" - ]; - - # Add uinput configuration - boot.kernelModules = [ "uinput" ]; - services.udev.extraRules = '' - KERNEL=="uinput", SUBSYSTEM=="misc", MODE="0660", GROUP="input" - ''; - - # Firewall rules - networking.firewall = { - extraCommands = lib.concatMapStrings (net: '' - iptables -A INPUT -p tcp -s ${net} -j ACCEPT - iptables -A INPUT -p udp -s ${net} -j ACCEPT - '') cfg.allowedNetworks; - }; - }; -} diff --git a/modules/system/hosts/fern/vfio/default.nix b/modules/system/hosts/fern/vfio/default.nix deleted file mode 100755 index 7c0f601..0000000 --- a/modules/system/hosts/fern/vfio/default.nix +++ /dev/null @@ -1,337 +0,0 @@ -{ - config, - pkgs, - lib, - ... -}: - -# TODO: make this module more generic, extendable, move to common -let - cfg = config.modules.system.fern.vfio; - - prime-run = pkgs.writeShellScriptBin "prime-run" '' - export __NV_PRIME_RENDER_OFFLOAD=1 - export __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0 - export __GLX_VENDOR_LIBRARY_NAME=nvidia - export __VK_LAYER_NV_optimus=NVIDIA_only - export DRI_PRIME=1 - export GBM_BACKEND=nvidia-drm - export __GLX_PRIME_RENDER_OFFLOAD=1 - export LIBVA_DRIVER_NAME=nvidia - export WLR_NO_HARDWARE_CURSORS=1 - exec "$@" - ''; -in -{ - options.modules.system.fern.vfio = { - enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Enable VFIO configuration"; - }; - }; - - config = lib.mkIf cfg.enable { - - system.activationScripts = { - postActivation = '' - # Create directories first - mkdir -p /home/richen/.local/bin - # Copy VFIO scripts and set permissions - cp -f ${./scripts/vfio.sh} /home/richen/.local/bin/vfio - cp -f ${./scripts/lg.sh} /home/richen/.local/bin/lg - cp -f ${./scripts/start-vfio.sh} /home/richen/.local/bin/start-vfio - cp -f ${./scripts/stop-vfio.sh} /home/richen/.local/bin/stop-vfio - cp -f ${./scripts/vm.sh} /home/richen/.local/bin/vm - cp -f ${./scripts/rdp.sh} /home/richen/.local/bin/rdp - - chown richen:users /home/richen/.local/bin/vfio - chown richen:users /home/richen/.local/bin/lg - chown richen:users /home/richen/.local/bin/start-vfio - chown richen:users /home/richen/.local/bin/stop-vfio - chown richen:users /home/richen/.local/bin/vm - chown richen:users /home/richen/.local/bin/rdp - chmod +x /home/richen/.local/bin/vfio - chmod +x /home/richen/.local/bin/lg - chmod +x /home/richen/.local/bin/start-vfio - chmod +x /home/richen/.local/bin/stop-vfio - chmod +x /home/richen/.local/bin/vm - chmod +x /home/richen/.local/bin/rdp - - # Create a new directory for environment variables - mkdir -p /etc/profile.d - - # Add scripts directory to system-wide PATH via profile.d - echo 'export PATH="/home/richen/.local/bin:$PATH"' > /etc/profile.d/vfio-scripts.sh - chmod +x /etc/profile.d/vfio-scripts.sh - ''; - }; - - services = { - spice-vdagentd.enable = true; - spice-webdavd.enable = true; - udev.extraRules = '' - SUBSYSTEM=="kvmfr", OWNER="richen", GROUP="kvm", MODE="0660" - ${builtins.readFile ./scripts/99-vendor-reset.rules} - ''; - }; - - networking = { - interfaces.br0 = { - useDHCP = true; - }; - bridges.br0 = { - interfaces = [ "enp7s0" ]; - rstp = true; - }; - firewall = { - allowedUDPPorts = [ - 53 - 67 - ]; - checkReversePath = false; - }; - networkmanager.unmanaged = [ - "br0" - "enp7s0" - ]; - }; - - users.users.richen = { - extraGroups = pkgs.lib.mkAfter [ - "wheel" - "networkmanager" - "video" - "libvirtd" - "kvm" - "qemu-libvirtd" - ]; - }; - - security = { - polkit = { - enable = true; - }; - sudo.extraRules = [ - { - groups = [ "wheel" ]; - commands = [ - { - command = "/home/richen/.local/bin/vfio"; - options = [ "NOPASSWD" ]; - } - { - command = "/home/richen/.local/bin/rdp"; - options = [ "NOPASSWD" ]; - } - { - command = "/home/richen/.local/bin/vm"; - options = [ "NOPASSWD" ]; - } - { - command = "/home/richen/.local/bin/lg"; - options = [ "NOPASSWD" ]; - } - ]; - } - ]; - }; - - # VFIO-related configurations - programs.virt-manager.enable = true; - systemd.tmpfiles.rules = [ - "d /dev/hugepages 1770 root kvm -" - "d /dev/shm 1777 root root -" - "f /dev/shm/looking-glass 0660 richen kvm -" - ]; - fileSystems."/dev/hugepages" = { - device = "hugetlbfs"; - fsType = "hugetlbfs"; - options = [ - "mode=01770" - "gid=kvm" - ]; - }; - - boot = { - kernelParams = [ - # Memory Management - "default_hugepagesz=2M" # Set default huge page size to 2MB - "hugepagesz=2M" # Configure huge page size as 2MB - "transparent_hugepage=never" # Disable transparent huge pages - "mem_sleep_default=deep" # Set default sleep mode to deep sleep - - # Boot Optimization - "fastboot" # Fast boot - "quiet" # Reduce boot verbosity - "rd.timeout=0" # Reduce initrd timeout - "rd.systemd.show_status=false" # Hide systemd status during boot - - # Performance & Security - "mitigations=off" # Disable CPU vulnerabilities mitigations (security trade-off) - "nowatchdog" # Disable watchdog timer - "nmi_watchdog=0" # Disable NMI watchdog - "split_lock_detect=off" # Disable split lock detection - "pcie_aspm=off" # Disable PCIe Active State Power Management - "amdgpu.dc=1" - "amdgpu.powerplay=1" - "amdgpu.ppfeaturemask=0xffffffff" - "radeon.modeset=0" - - # IOMMU & VFIO - "intel_iommu=on" # Enable Intel IOMMU - "iommu=pt" # Enable IOMMU pass-through - "vfio-pci.ids=10de:2782,10de:22bc" # Specify VFIO PCI device IDs - - # KVM Settings - "kvm.ignore_msrs=1" # Ignore unhandled Model Specific Registers - "kvm.report_ignored_msrs=0" # Don't report ignored MSRs - - # ACPI & Power Management - "acpi_osi=Linux" # Set ACPI OS interface to Linux - "acpi=force" # Force ACPI - "resume_offset=0" # Set resume offset to 0 - ]; - kernelModules = [ - "vfio_pci" - "vfio" - "vfio_iommu_type1" - "kvmfr" - "kvm-intel" - "kvm" - "amdgpu" - ]; - initrd.kernelModules = [ - "amdgpu" - "vfio_pci" - "vfio" - "vfio_iommu_type1" - ]; - extraModulePackages = with config.boot.kernelPackages; [ - kvmfr - ]; - blacklistedKernelModules = [ - "nouveau" - "nvidia" - "nvidia_drm" - "nvidia_modeset" - "nvidia_uvm" - ]; - extraModprobeConfig = '' - options kvmfr static_size_mb=64 - blacklist nouveau - options nouveau modeset=0 - ''; - }; - - virtualisation = { - libvirtd = { - enable = true; - hooks = { - qemu = { - # TODO: figure out the prepare script - # "prepare" = ./modules/vfio/start.sh; - # "release" = ./modules/vfio/stop.sh; - }; - }; - qemu = { - ovmf = { - enable = true; - packages = [ - (pkgs.OVMF.override { - secureBoot = true; - tpmSupport = true; - }) - ]; - }; - swtpm.enable = true; - runAsRoot = true; - package = pkgs.qemu_kvm; - verbatimConfig = '' - user = "richen" - group = "kvm" - cgroup_device_acl = [ - "/dev/null", "/dev/full", "/dev/zero", - "/dev/random", "/dev/urandom", - "/dev/ptmx", "/dev/kvm", "/dev/kqemu", - "/dev/rtc","/dev/hpet", "/dev/sev", - "/dev/kvmfr0", - "/dev/vfio/vfio" - ] - hugetlbfs_mount = "/dev/hugepages" - bridge_helper = "/run/wrappers/bin/qemu-bridge-helper" - ''; - }; - }; - spiceUSBRedirection.enable = true; - }; - - users.groups.libvirtd.members = [ "richen" ]; - users.groups.kvm.members = [ "richen" ]; - - systemd.services.define-win11-vm = { - description = "Define Windows 11 VM"; - after = [ "libvirtd.service" ]; - requires = [ "libvirtd.service" ]; - wantedBy = [ "multi-user.target" ]; - - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - User = "root"; - }; - - script = '' - # Ensure NVRAM directory exists with proper permissions - mkdir -p /var/lib/libvirt/qemu/nvram - chown -R richen:kvm /var/lib/libvirt/qemu/nvram - chmod 775 /var/lib/libvirt/qemu/nvram - - # Create symlink directory for OVMF files - mkdir -p /var/libvirt/nix-ovmf - ln -sf ${pkgs.OVMF.fd}/FV/OVMF_CODE.fd /var/libvirt/nix-ovmf/OVMF_CODE.fd - ln -sf ${pkgs.OVMF.fd}/FV/OVMF_VARS.fd /var/libvirt/nix-ovmf/OVMF_VARS.fd - chown -R richen:kvm /var/libvirt/nix-ovmf - chmod -R 775 /var/libvirt/nix-ovmf - - # Copy OVMF NVRAM template if it doesn't exist - if [ ! -f /var/lib/libvirt/qemu/nvram/win11_VARS.fd ]; then - cp ${pkgs.OVMF.fd}/FV/OVMF_VARS.fd /var/lib/libvirt/qemu/nvram/win11_VARS.fd - chown richen:kvm /var/lib/libvirt/qemu/nvram/win11_VARS.fd - chmod 660 /var/lib/libvirt/qemu/nvram/win11_VARS.fd - fi - - # Check if VM already exists - if ! ${pkgs.libvirt}/bin/virsh list --all --name | grep -q "^win11$"; then - ${pkgs.libvirt}/bin/virsh define ${./scripts/win11.xml} - fi - ''; - }; - - environment.systemPackages = with pkgs; [ - # Add prime-run script as package - prime-run - # -------------------- Virtualization & VFIO -------------------- - qemu - virt-manager # Virtual machine manager - virt-viewer # Virtual machine viewer - libvirt # Virtualization API - spice-gtk # Remote display - spice-protocol # Spice protocol - spice-vdagent # Spice vdagent - win-virtio # Windows virtio drivers - win-spice # Windows spice drivers - OVMF # UEFI firmware - OVMFFull # UEFI firmware (with extra features) - looking-glass-client # VFIO display - freerdp3 # RDP client - - udisks # Storage device daemon - udiskie # Automounter - ntfs3g # NTFS filesystem support - cpuset # CPU management - kmod # Kernel module management - inotify-tools # File change notification - ]; - }; -} diff --git a/modules/system/hosts/fern/vfio/scripts/99-vendor-reset.rules b/modules/system/hosts/fern/vfio/scripts/99-vendor-reset.rules deleted file mode 100755 index 7efe6bd..0000000 --- a/modules/system/hosts/fern/vfio/scripts/99-vendor-reset.rules +++ /dev/null @@ -1,135 +0,0 @@ -# Rules to ensure vendor-reset is loaded and the reset_method for our devices is set to 'device_specific' for kernel 5.15+ -# (the module must be loaded, otherwise setting this may fail) -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67C0", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67C1", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67C2", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67C4", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67C7", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67D0", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67DF", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67C8", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67C9", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67CA", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67CC", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67CF", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6FDF", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67E0", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67E3", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67E8", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67EB", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67EF", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67FF", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67E1", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67E7", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x67E9", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6980", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6981", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6985", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6986", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6987", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6995", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6997", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x699F", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6860", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6861", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6862", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6863", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6864", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6867", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6868", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x6869", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x686a", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x686b", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x686c", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x686d", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x686e", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x686f", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x687f", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x66a0", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x66a1", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x66a2", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x66a3", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x66a4", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x66a7", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x66af", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x7310", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x7312", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x7318", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x7319", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x731a", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x731b", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x731e", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x731f", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x7340", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x7341", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x7347", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x734F", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x7360", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x7362", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" - -ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1002", ATTR{device}=="0x738c", RUN+="/bin/sh -c '/sbin/modprobe vendor-reset; echo device_specific > /sys$env{DEVPATH}/reset_method'" diff --git a/modules/system/hosts/fern/vfio/scripts/lg.sh b/modules/system/hosts/fern/vfio/scripts/lg.sh deleted file mode 100755 index 1188250..0000000 --- a/modules/system/hosts/fern/vfio/scripts/lg.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -# Check if the win11 VM is running -if ! sudo virsh list --state-running --name | grep -q "win11"; then - echo "The win11 VM is not running. Please start it before connecting via looking glass." - exit 1 -fi - -# Launch looking-glass-client with F10 as the mouse capture key -nohup looking-glass-client -f /dev/kvmfr0 -m 59 >/dev/null 2>&1 & diff --git a/modules/system/hosts/fern/vfio/scripts/rdp.sh b/modules/system/hosts/fern/vfio/scripts/rdp.sh deleted file mode 100755 index 9cd77c6..0000000 --- a/modules/system/hosts/fern/vfio/scripts/rdp.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash - -# Check if the win11 VM is running -if ! sudo virsh list --state-running --name | grep -q "win11"; then - echo "The win11 VM is not running. Please start it before connecting via RDP." - exit 1 -fi - -# Prompt for password and hide input -read -sp "Enter password: " password -echo # Add a newline after password input - -# Check if RDP is running -if ! pgrep -f "xfreerdp" > /dev/null; then - echo "RDP is not running. Starting RDP..." - nohup xfreerdp \ - /v:10.0.0.172 \ - /u:richard \ - /p:"$password" \ - +dynamic-resolution \ - /gfx:avc444 \ - /network:auto \ - /compression-level:2 \ - /sound \ - /microphone \ - +clipboard \ - +fonts \ - +aero \ - +window-drag \ - +menu-anims \ - +themes \ - /cert:ignore > /dev/null 2>&1 & - echo "RDP started in the background." -else - echo "RDP is already running." -fi diff --git a/modules/system/hosts/fern/vfio/scripts/start-vfio.sh b/modules/system/hosts/fern/vfio/scripts/start-vfio.sh deleted file mode 100755 index e8fbdc4..0000000 --- a/modules/system/hosts/fern/vfio/scripts/start-vfio.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/run/current-system/sw/bin/bash -# https://rokups.github.io/#!pages/gaming-vm-performance.md - -set -e - -log() { - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a /var/log/libvirt/hooks.log >&2 -} - -error() { - log "ERROR: $1" - exit 1 -} - -main() { - # Check if running as root - if [[ $EUID -ne 0 ]]; then - error "This script must be run as root" - fi - - log "Starting VM preparation..." - - # Reset PCI device - if [[ -e /sys/bus/pci/devices/0000:08:00.0/reset ]]; then - echo 1 > /sys/bus/pci/devices/0000:08:00.0/reset || error "Failed to reset PCI device" - else - log "PCI device reset file not found, skipping reset" - fi - - HOST_CORES='12-19' # 2 P-cores (threads 12-15) + 4 E-cores (threads 16-19) - VIRT_CORES='0-11' # 6 P-cores (threads 0-11) - - # Function to convert core range to hexadecimal mask - cores_to_mask() { - local cores="$1" - local mask=0 - for core in $(seq ${cores/-/ }); do - mask=$((mask | 1< /dev/null 2>&1 || true - done - } - - # Set CPU affinity for systemd slices - systemctl set-property --runtime -- user.slice AllowedCPUs=$HOST_CORES || log "Failed to set CPU affinity for user.slice" - systemctl set-property --runtime -- system.slice AllowedCPUs=$HOST_CORES || log "Failed to set CPU affinity for system.slice" - systemctl set-property --runtime -- init.scope AllowedCPUs=$HOST_CORES || log "Failed to set CPU affinity for init.scope" - - # Drop caches and compact memory before allocating hugepages - sync - sysctl -w vm.drop_caches=3 || log "Failed to drop caches" - sysctl -w vm.compact_memory=1 || log "Failed to compact memory" - - # Hugepages allocation - sysctl -w vm.nr_hugepages=31744 || log "Failed to allocate hugepages" - - # Shield VM cores - pin_vm_cores - - # Reduce VM jitter and set other kernel parameters - sysctl -w vm.stat_interval=120 || log "Failed to set vm.stat_interval" - sysctl -w kernel.watchdog=0 || log "Failed to disable kernel watchdog" - echo $HOST_CORES_MASK > /sys/bus/workqueue/devices/writeback/cpumask || log "Failed to set writeback cpumask" - - # Check if transparent hugepages are available - if [[ -f /sys/kernel/mm/transparent_hugepage/enabled ]]; then - echo never > /sys/kernel/mm/transparent_hugepage/enabled || log "Failed to disable THP" - else - log "Transparent hugepages not available, skipping THP disable" - fi - - # Force P-states to P0 - for governor in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do - echo performance > "$governor" || log "Failed to set CPU governor to performance" - done - - log "VM preparation completed successfully" -} - -main - -# TODO: Implement NVIDIA driver check and VFIO setup for NixOS -# This part needs to be adapted to NixOS-specific commands and paths diff --git a/modules/system/hosts/fern/vfio/scripts/stop-vfio.sh b/modules/system/hosts/fern/vfio/scripts/stop-vfio.sh deleted file mode 100755 index eea3e07..0000000 --- a/modules/system/hosts/fern/vfio/scripts/stop-vfio.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/run/current-system/sw/bin/bash -# https://rokups.github.io/#!pages/gaming-vm-performance.md - - -TOTAL_CORES='0-19' -TOTAL_CORES_MASK=FFFFF # bitmask for all 20 threads -HOST_CORES='12-19' # 2 P-cores (threads 12-15) + 4 E-cores (threads 16-19) -HOST_CORES_MASK=FF000 # bitmask 0b11111111000000000000 -VIRT_CORES='0-11' # 6 P-cores (threads 0-11) -VIRT_CORES_MASK=00FFF # bitmask 0b00000000111111111111 - -unpin_cores() { - # Reset all tasks to use all cores - for pid in $(ps -eo pid --no-headers); do - taskset -pc $TOTAL_CORES $pid > /dev/null 2>&1 - done -} - -# Reset CPU affinity for systemd slices -systemctl set-property --runtime -- user.slice AllowedCPUs=$TOTAL_CORES -systemctl set-property --runtime -- system.slice AllowedCPUs=$TOTAL_CORES -systemctl set-property --runtime -- init.scope AllowedCPUs=$TOTAL_CORES - -# All VMs offline -sysctl vm.stat_interval=1 -sysctl -w kernel.watchdog=1 -unpin_cores - -# Reset writeback workqueue to use all cores -echo $TOTAL_CORES_MASK > /sys/bus/workqueue/devices/writeback/cpumask - -# Hugepages deallocation -echo 0 | tee /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages -echo 0 | tee /proc/sys/vm/nr_hugepages - -echo always | tee /sys/kernel/mm/transparent_hugepage/enabled -echo powersave | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor - -echo >&2 "VMs Unpinned" - -# TODO: Implement NVIDIA driver reattachment and VFIO cleanup for NixOS -# This part needs to be adapted to NixOS-specific commands and paths diff --git a/modules/system/hosts/fern/vfio/scripts/vfio.sh b/modules/system/hosts/fern/vfio/scripts/vfio.sh deleted file mode 100755 index 4f1773f..0000000 --- a/modules/system/hosts/fern/vfio/scripts/vfio.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env bash - -NVIDIA_GPU="0000:08:00.0" -NVIDIA_AUDIO="0000:08:00.1" - -bind_device() { - local device=$1 - local from_driver=$2 - local to_driver=$3 - - echo "Unbinding $device from $from_driver..." - if [ "$from_driver" = "nvidia" ]; then - # Disable DRM modesetting before unbinding - if lsmod | grep -q nvidia_drm; then - sudo rmmod nvidia_drm - sudo rmmod nvidia_uvm - sudo rmmod nvidia_modeset - sudo rmmod nvidia - fi - fi - - if [ -e "/sys/bus/pci/drivers/$from_driver/$device" ]; then - echo -n "$device" | sudo tee "/sys/bus/pci/drivers/$from_driver/unbind" >/dev/null - fi - - # Make sure the new driver is loaded - if [ "$to_driver" = "nvidia" ]; then - # Remove vfio drivers first - sudo modprobe -r vfio-pci vfio_iommu_type1 vfio - # Load NVIDIA drivers in correct order - sudo modprobe nvidia - sudo modprobe nvidia_modeset - sudo modprobe nvidia_uvm - sudo modprobe nvidia_drm modeset=1 - elif [ "$to_driver" = "vfio-pci" ]; then - # NVIDIA drivers are already unloaded above - sudo modprobe vfio-pci vfio_iommu_type1 vfio - fi - - # Remove the device ID from the old driver and add it to the new one - echo "Binding $device to $to_driver..." - echo -n "$device" | sudo tee "/sys/bus/pci/drivers/$to_driver/new_id" >/dev/null 2>&1 || true - echo -n "$device" | sudo tee "/sys/bus/pci/drivers/$to_driver/bind" >/dev/null - - # Verify the binding - if [ -e "/sys/bus/pci/drivers/$to_driver/$device" ]; then - echo "Successfully bound $device to $to_driver" - else - echo "Failed to bind $device to $to_driver" - fi -} - -bind_gpu() { - # Bind GPU - bind_device "$NVIDIA_GPU" "nvidia" "vfio-pci" - sleep 1 - # Bind Audio - bind_device "$NVIDIA_AUDIO" "snd_hda_intel" "vfio-pci" # Changed from nvidia to snd_hda_intel - echo "GPU and Audio devices binding completed" -} - -unbind_gpu() { - # Unbind Audio - bind_device "$NVIDIA_AUDIO" "vfio-pci" "snd_hda_intel" # Changed to snd_hda_intel - sleep 1 - # Unbind GPU - bind_device "$NVIDIA_GPU" "vfio-pci" "nvidia" - echo "GPU and Audio devices unbinding completed" -} - -status() { - echo "Checking device status..." - echo - - echo "GPU ($NVIDIA_GPU):" - if [ -e "/sys/bus/pci/devices/$NVIDIA_GPU" ]; then - driver=$(readlink "/sys/bus/pci/devices/$NVIDIA_GPU/driver" 2>/dev/null) - driver=${driver##*/} - echo " Driver in use: ${driver:-None}" - else - echo " Not found" - fi - - echo - echo "Audio ($NVIDIA_AUDIO):" - if [ -e "/sys/bus/pci/devices/$NVIDIA_AUDIO" ]; then - driver=$(readlink "/sys/bus/pci/devices/$NVIDIA_AUDIO/driver" 2>/dev/null) - driver=${driver##*/} - echo " Driver in use: ${driver:-None}" - else - echo " Not found" - fi -} - -case "$1" in - bind) - bind_gpu - ;; - unbind) - unbind_gpu - ;; - status) - status - ;; - *) - echo "Usage: $0 {bind|unbind|status}" - exit 1 - ;; -esac - -exit 0 \ No newline at end of file diff --git a/modules/system/hosts/fern/vfio/scripts/vm.sh b/modules/system/hosts/fern/vfio/scripts/vm.sh deleted file mode 100755 index e86f100..0000000 --- a/modules/system/hosts/fern/vfio/scripts/vm.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env bash - -# Function to check if Win11 VM is running -check_win11_vm() { - sudo virsh list --all | grep -q "win11.*running" -} - -# Function to display VM status and available commands -show_status() { - echo "Windows 11 VM Status:" - if check_win11_vm; then - echo " Status: Running" - else - echo " Status: Stopped" - fi - echo - echo "Available commands:" - echo " vm - Show this status" - echo " vm start - Start the VM" - echo " vm stop - Stop the VM" -} - -# Function to start Win11 VM -start_win11_vm() { - if check_win11_vm; then - echo "VM is already running" - return 1 - fi - - echo "Starting Windows 11 VM..." - sudo start-vfio - sudo virsh start win11 - - # Set up trap to wait for VM shutdown using inotifywait with proper cleanup - nohup bash -c ' - # Create a unique PID file for this monitor instance - MONITOR_PID_FILE="/tmp/vm_monitor_$$.pid" - echo $$ > "$MONITOR_PID_FILE" - - # Cleanup function - cleanup() { - rm -f "$MONITOR_PID_FILE" - exit 0 - } - - # Set trap for cleanup - trap cleanup EXIT - - echo "$(date): Waiting for VM shutdown" >> /tmp/vm_shutdown.log - if inotifywait -e delete /var/run/libvirt/qemu/win11.pid; then - echo "$(date): VM shutdown detected" >> /tmp/vm_shutdown.log - sudo stop-vfio - echo "$(date): VFIO stopped" >> /tmp/vm_shutdown.log - else - echo "$(date): inotifywait failed" >> /tmp/vm_shutdown.log - fi - ' >/dev/null 2>&1 & - - # Store the monitor process PID - MONITOR_PID=$! - echo "Background monitoring process started with PID $MONITOR_PID" -} - -# Function to stop Win11 VM -stop_win11_vm() { - if ! check_win11_vm; then - echo "VM is not running" - return 1 - fi - - echo "Stopping Windows 11 VM..." - sudo virsh shutdown win11 - sudo stop-vfio - - # Cleanup any existing monitor processes - for pid_file in /tmp/vm_monitor_*.pid; do - if [ -f "$pid_file" ]; then - pid=$(cat "$pid_file") - kill $pid 2>/dev/null || true - rm -f "$pid_file" - fi - done -} - -# Main script command handling -case "${1:-status}" in -start) - start_win11_vm - ;; -stop) - stop_win11_vm - ;; -status | "") - show_status - ;; -*) - echo "Unknown command: $1" - echo "Usage: $0 [start|stop|status]" - exit 1 - ;; -esac diff --git a/modules/system/hosts/fern/vfio/scripts/win11.xml b/modules/system/hosts/fern/vfio/scripts/win11.xml deleted file mode 100755 index fc3fd86..0000000 --- a/modules/system/hosts/fern/vfio/scripts/win11.xml +++ /dev/null @@ -1,291 +0,0 @@ - - win11 - 8431eb34-6047-49f9-85a7-02886f1da29b - - - - - - 32768000 - 32768000 - - - - 12 - - - - - - - - - - - - - - - - - hvm - /var/libvirt/nix-ovmf/OVMF_CODE.fd - /var/lib/libvirt/qemu/nvram/win11_VARS.fd - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - destroy - restart - destroy - - - - - - /run/current-system/sw/bin/qemu-system-x86_64 - - - - - - -
- - - - - - -
- - -
- - - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - -
- - -
- - -
- - -
- - - - - - -
- - - - - - - - - - - -
- - -
- - -
- - - - - - - - - -