diff --git a/.envrc b/.envrc index a6b1f81..f5141c2 100644 --- a/.envrc +++ b/.envrc @@ -1,4 +1,3 @@ -# shellcheck shell=bash if ! has nix_direnv_version || ! nix_direnv_version 3.0.0; then source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.0/direnvrc" "sha256-21TMnI2xWX7HkSTjFFri2UaohXVj854mgvWapWrxRXg=" fi diff --git a/flake.lock b/flake.lock index fd6173b..55d50b0 100644 --- a/flake.lock +++ b/flake.lock @@ -14,11 +14,11 @@ ] }, "locked": { - "lastModified": 1762618334, - "narHash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ=", + "lastModified": 1723293904, + "narHash": "sha256-b+uqzj+Wa6xgMS9aNbX4I+sXeb5biPDi39VgvSFqFvU=", "owner": "ryantm", "repo": "agenix", - "rev": "fcdea223397448d35d9b31f798479227e80183f6", + "rev": "f6291c5935fdc4e0bef208cfc0dcab7e3f7a1c41", "type": "github" }, "original": { @@ -36,11 +36,11 @@ ] }, "locked": { - "lastModified": 1744478979, - "narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=", + "lastModified": 1700795494, + "narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=", "owner": "lnl7", "repo": "nix-darwin", - "rev": "43975d782b418ebf4969e9ccba82466728c2851b", + "rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d", "type": "github" }, "original": { @@ -53,11 +53,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1761588595, - "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -73,11 +73,11 @@ ] }, "locked": { - "lastModified": 1763759067, - "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=", + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", "type": "github" }, "original": { @@ -108,33 +108,10 @@ "type": "github" } }, - "git-hooks": { - "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1765464257, - "narHash": "sha256-dixPWKiHzh80PtD0aLuxYNQ0xP+843dfXG/yM3OzaYQ=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "09e45f2598e1a8499c3594fe11ec2943f34fe509", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "master", - "repo": "git-hooks.nix", - "type": "github" - } - }, "gitignore": { "inputs": { "nixpkgs": [ - "git-hooks", + "pre-commit-hooks", "nixpkgs" ] }, @@ -159,11 +136,11 @@ ] }, "locked": { - "lastModified": 1765480374, - "narHash": "sha256-HlbvQAqLx7WqZFFQZ8nu5UUJAVlXiV/kqKbyueA8srw=", + "lastModified": 1732482255, + "narHash": "sha256-GUffLwzawz5WRVfWaWCg78n/HrBJrOG7QadFY6rtV8A=", "owner": "nix-community", "repo": "home-manager", - "rev": "39cb677ed9e908e90478aa9fe5f3383dfc1a63f3", + "rev": "a9953635d7f34e7358d5189751110f87e3ac17da", "type": "github" }, "original": { @@ -173,13 +150,29 @@ "type": "github" } }, + "impermanence": { + "locked": { + "lastModified": 1697303681, + "narHash": "sha256-caJ0rXeagaih+xTgRduYtYKL1rZ9ylh06CIrt1w5B4g=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "0f317c2e9e56550ce12323eb39302d251618f5b5", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "master", + "repo": "impermanence", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1765186076, - "narHash": "sha256-hM20uyap1a0M9d344I692r+ik4gTMyj60cQWO+hAYP8=", + "lastModified": 1732521221, + "narHash": "sha256-2ThgXBUXAE1oFsVATK1ZX9IjPcS4nKFOAjhPNKuiMn0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "addf7cf5f383a3101ecfba091b98d0a1263dc9b8", + "rev": "4633a7c72337ea8fd23a4f2ba3972865e3ec685d", "type": "github" }, "original": { @@ -190,38 +183,57 @@ } }, "nur": { - "inputs": { - "flake-parts": [ - "flake-parts" - ], - "nixpkgs": [ - "nixpkgs" - ] - }, "locked": { - "lastModified": 1765481746, - "narHash": "sha256-oWDp4EMOXvPZSC5ZVdg90K7EFgUGvxmrFAwA/1hJ/j4=", + "lastModified": 1732704680, + "narHash": "sha256-x3NlO2qzuobU9BrynzydX7X9oskJpysv7BI7DJ5cVSE=", "owner": "nix-community", "repo": "NUR", - "rev": "2b2d6d53d6a66d1be2d8620024cc61ad986bcee2", + "rev": "31a30f0862fd8b5f88a6597382bb09197356b19e", "type": "github" }, "original": { "owner": "nix-community", - "ref": "main", + "ref": "master", "repo": "NUR", "type": "github" } }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1732021966, + "narHash": "sha256-mnTbjpdqF0luOkou8ZFi2asa1N3AA2CchR/RqCNmsGE=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "3308484d1a443fc5bc92012435d79e80458fe43c", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "master", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { "agenix": "agenix", "flake-parts": "flake-parts", "futils": "futils", - "git-hooks": "git-hooks", "home-manager": "home-manager", + "impermanence": "impermanence", "nixpkgs": "nixpkgs", "nur": "nur", + "pre-commit-hooks": "pre-commit-hooks", "systems": "systems" } }, diff --git a/flake.nix b/flake.nix index 0bdd180..5a5b0ef 100644 --- a/flake.nix +++ b/flake.nix @@ -43,6 +43,13 @@ }; }; + impermanence = { + type = "github"; + owner = "nix-community"; + repo = "impermanence"; + ref = "master"; + }; + nixpkgs = { type = "github"; owner = "NixOS"; @@ -54,20 +61,17 @@ type = "github"; owner = "nix-community"; repo = "NUR"; - ref = "main"; - inputs = { - flake-parts.follows = "flake-parts"; - nixpkgs.follows = "nixpkgs"; - }; + ref = "master"; }; - git-hooks = { + pre-commit-hooks = { type = "github"; owner = "cachix"; - repo = "git-hooks.nix"; + repo = "pre-commit-hooks.nix"; ref = "master"; inputs = { nixpkgs.follows = "nixpkgs"; + nixpkgs-stable.follows = "nixpkgs"; }; }; diff --git a/flake/checks.nix b/flake/checks.nix index 73e64d5..98e49bd 100644 --- a/flake/checks.nix +++ b/flake/checks.nix @@ -1,7 +1,7 @@ { inputs, ... }: { imports = [ - inputs.git-hooks.flakeModule + inputs.pre-commit-hooks.flakeModule ]; perSystem = { ... }: { diff --git a/flake/dev-shells.nix b/flake/dev-shells.nix index 87464a4..d5f5989 100644 --- a/flake/dev-shells.nix +++ b/flake/dev-shells.nix @@ -6,6 +6,7 @@ name = "NixOS-config"; nativeBuildInputs = with pkgs; [ + gitAndTools.pre-commit nixpkgs-fmt ]; diff --git a/flake/home-manager.nix b/flake/home-manager.nix index 88a74e8..34af375 100644 --- a/flake/home-manager.nix +++ b/flake/home-manager.nix @@ -3,11 +3,6 @@ let defaultModules = [ # Include generic settings "${self}/modules/home" - { - nixpkgs.overlays = (lib.attrValues self.overlays) ++ [ - inputs.nur.overlays.default - ]; - } { # Basic user information defaults home.username = lib.mkDefault "ambroisie"; @@ -22,15 +17,22 @@ let ]; mkHome = name: system: inputs.home-manager.lib.homeManagerConfiguration { - pkgs = inputs.nixpkgs.legacyPackages.${system}; + # Work-around for home-manager + # * not letting me set `lib` as an extraSpecialArgs + # * not respecting `nixpkgs.overlays` [1] + # [1]: https://github.com/nix-community/home-manager/issues/2954 + pkgs = import inputs.nixpkgs { + inherit system; + + overlays = (lib.attrValues self.overlays) ++ [ + inputs.nur.overlay + ]; + }; modules = defaultModules ++ [ "${self}/hosts/homes/${name}" ]; - # Use my extended lib in NixOS configuration - inherit (self) lib; - extraSpecialArgs = { # Inject inputs to use them in global registry inherit inputs; diff --git a/flake/nixos.nix b/flake/nixos.nix index 0fbd3a6..b48b551 100644 --- a/flake/nixos.nix +++ b/flake/nixos.nix @@ -3,11 +3,11 @@ let defaultModules = [ { # Let 'nixos-version --json' know about the Git revision - system.configurationRevision = self.rev or self.dirtyRev or "dirty"; + system.configurationRevision = self.rev or "dirty"; } { nixpkgs.overlays = (lib.attrValues self.overlays) ++ [ - inputs.nur.overlays.default + inputs.nur.overlay ]; } # Include generic settings @@ -15,10 +15,8 @@ let ]; buildHost = name: system: lib.nixosSystem { + inherit system; modules = defaultModules ++ [ - { - nixpkgs.hostPlatform = system; - } "${self}/hosts/nixos/${name}" ]; specialArgs = { diff --git a/flake/overlays.nix b/flake/overlays.nix index c10afc3..0c47989 100644 --- a/flake/overlays.nix +++ b/flake/overlays.nix @@ -1,4 +1,4 @@ -{ self, lib, ... }: +{ self, ... }: let default-overlays = import "${self}/overlays"; @@ -8,7 +8,7 @@ let # Expose my custom packages pkgs = _final: prev: { - ambroisie = lib.recurseIntoAttrs (import "${self}/pkgs" { pkgs = prev; }); + ambroisie = prev.recurseIntoAttrs (import "${self}/pkgs" { pkgs = prev; }); }; }; in diff --git a/hosts/homes/ambroisie@bazin/default.nix b/hosts/homes/ambroisie@bazin/default.nix index 365b70d..f52fbce 100644 --- a/hosts/homes/ambroisie@bazin/default.nix +++ b/hosts/homes/ambroisie@bazin/default.nix @@ -4,20 +4,6 @@ services.gpg-agent.enable = lib.mkForce false; my.home = { - atuin = { - package = pkgs.stdenv.mkDerivation { - pname = "atuin"; - version = "18.4.0"; - - buildCommand = '' - mkdir -p $out/bin - ln -s /usr/bin/atuin $out/bin/atuin - ''; - - meta.mainProgram = "atuin"; - }; - }; - git = { package = pkgs.emptyDirectory; }; diff --git a/hosts/homes/ambroisie@mousqueton/default.nix b/hosts/homes/ambroisie@mousqueton/default.nix index 1383618..44e62e6 100644 --- a/hosts/homes/ambroisie@mousqueton/default.nix +++ b/hosts/homes/ambroisie@mousqueton/default.nix @@ -7,20 +7,6 @@ services.gpg-agent.enable = lib.mkForce false; my.home = { - atuin = { - package = pkgs.stdenv.mkDerivation { - pname = "atuin"; - version = "18.4.0"; - - buildCommand = '' - mkdir -p $out/bin - ln -s /usr/bin/atuin $out/bin/atuin - ''; - - meta.mainProgram = "atuin"; - }; - }; - git = { package = pkgs.emptyDirectory; }; @@ -29,9 +15,6 @@ # I use scripts that use the passthrough sequence often on this host enablePassthrough = true; - # Frequent reboots mean that session persistence can be handy - enableResurrect = true; - terminalFeatures = { # HTerm uses `xterm-256color` as its `$TERM`, so use that here xterm-256color = { }; diff --git a/hosts/nixos/aramis/home.nix b/hosts/nixos/aramis/home.nix index e8c99e4..64b63ce 100644 --- a/hosts/nixos/aramis/home.nix +++ b/hosts/nixos/aramis/home.nix @@ -18,7 +18,9 @@ # Machine specific packages packages.additionalPackages = with pkgs; [ element-desktop # Matrix client + jellyfin-media-player # Wraps the webui and mpv together pavucontrol # Audio mixer GUI + transgui # Transmission remote ]; # Minimal video player mpv.enable = true; @@ -26,8 +28,6 @@ nm-applet.enable = true; # Terminal terminal.program = "alacritty"; - # Transmission remote - trgui.enable = true; # Zathura document viewer zathura.enable = true; }; diff --git a/hosts/nixos/porthos/secrets/secrets.nix b/hosts/nixos/porthos/secrets/secrets.nix index f1842b4..68e90f2 100644 --- a/hosts/nixos/porthos/secrets/secrets.nix +++ b/hosts/nixos/porthos/secrets/secrets.nix @@ -80,12 +80,18 @@ in "pyload/credentials.age".publicKeys = all; - "servarr/autobrr/session-secret.age".publicKeys = all; - "servarr/cross-seed/configuration.json.age".publicKeys = all; - - "sso/auth-key.age".publicKeys = all; - "sso/ambroisie/password-hash.age".publicKeys = all; - "sso/ambroisie/totp-secret.age".publicKeys = all; + "sso/auth-key.age" = { + owner = "nginx-sso"; + publicKeys = all; + }; + "sso/ambroisie/password-hash.age" = { + owner = "nginx-sso"; + publicKeys = all; + }; + "sso/ambroisie/totp-secret.age" = { + owner = "nginx-sso"; + publicKeys = all; + }; "tandoor-recipes/secret-key.age".publicKeys = all; diff --git a/hosts/nixos/porthos/secrets/servarr/autobrr/session-secret.age b/hosts/nixos/porthos/secrets/servarr/autobrr/session-secret.age deleted file mode 100644 index e98b94a..0000000 --- a/hosts/nixos/porthos/secrets/servarr/autobrr/session-secret.age +++ /dev/null @@ -1,7 +0,0 @@ -age-encryption.org/v1 --> ssh-ed25519 cKojmg bu09lB+fjaPP31cUQZP6EqSPuseucgNK7k9vAS08iS0 -+NGL+b2QD/qGo6hqHvosAXzHZtDvfodmPdcgnrKlD1o --> ssh-ed25519 jPowng QDCdRBGWhtdvvMCiDH52cZHz1/W7aomhTatZ4+9IKwI -Ou3jjV/O55G1CPgGS33l3eWhhYWrVdwVNPSiE14d5rE ---- q0ssmpG50OX1WaNSInc2hbtH3DbTwQGDU74VGEoMh94 -mCƑ'hK./Xu(g$'M{fK !MZoR՝͟;yb \ No newline at end of file diff --git a/hosts/nixos/porthos/secrets/servarr/cross-seed/configuration.json.age b/hosts/nixos/porthos/secrets/servarr/cross-seed/configuration.json.age deleted file mode 100644 index 94fdf97..0000000 Binary files a/hosts/nixos/porthos/secrets/servarr/cross-seed/configuration.json.age and /dev/null differ diff --git a/hosts/nixos/porthos/services.nix b/hosts/nixos/porthos/services.nix index 96f15d3..ffd150a 100644 --- a/hosts/nixos/porthos/services.nix +++ b/hosts/nixos/porthos/services.nix @@ -51,9 +51,9 @@ in passwordFile = secrets."forgejo/mail-password".path; }; }; - # Home inventory - homebox = { - enable = true; + # Meta-indexers + indexers = { + prowlarr.enable = true; }; # Jellyfin media server jellyfin.enable = true; @@ -144,24 +144,11 @@ in sabnzbd.enable = true; # The whole *arr software suite servarr = { - enableAll = true; - autobrr = { - sessionSecretFile = secrets."servarr/autobrr/session-secret".path; - }; - cross-seed = { - secretSettingsFile = secrets."servarr/cross-seed/configuration.json".path; - }; + enable = true; # ... But not Lidarr because I don't care for music that much lidarr = { enable = false; }; - # I only use Prowlarr nowadays - jackett = { - enable = false; - }; - nzbhydra = { - enable = false; - }; }; # Because I still need to play sysadmin ssh-server.enable = true; diff --git a/modules/home/atuin/default.nix b/modules/home/atuin/default.nix index 40d2b04..3f06263 100644 --- a/modules/home/atuin/default.nix +++ b/modules/home/atuin/default.nix @@ -6,11 +6,8 @@ in options.my.home.atuin = with lib; { enable = my.mkDisableOption "atuin configuration"; + # I want the full experience by default package = mkPackageOption pkgs "atuin" { }; - - daemon = { - enable = my.mkDisableOption "atuin daemon"; - }; }; config = lib.mkIf cfg.enable { @@ -18,18 +15,12 @@ in enable = true; inherit (cfg) package; - daemon = lib.mkIf cfg.daemon.enable { - enable = true; - }; - flags = [ # I *despise* this hijacking of the up key, even though I use Ctrl-p "--disable-up-arrow" ]; settings = { - # Reasonable date format - dialect = "uk"; # The package is managed by Nix update_check = false; # I don't care for the fancy display diff --git a/modules/home/default.nix b/modules/home/default.nix index ad3b979..c8183cf 100644 --- a/modules/home/default.nix +++ b/modules/home/default.nix @@ -8,7 +8,6 @@ ./bluetooth ./calibre ./comma - ./delta ./dircolors ./direnv ./discord @@ -38,7 +37,6 @@ ./ssh ./terminal ./tmux - ./trgui ./udiskie ./vim ./wget @@ -52,6 +50,9 @@ # First sane reproducible version home.stateVersion = "20.09"; + # Who am I? + home.username = "ambroisie"; + # Start services automatically systemd.user.startServices = "sd-switch"; } diff --git a/modules/home/delta/default.nix b/modules/home/delta/default.nix deleted file mode 100644 index e76edc6..0000000 --- a/modules/home/delta/default.nix +++ /dev/null @@ -1,49 +0,0 @@ -{ config, pkgs, lib, ... }: -let - cfg = config.my.home.delta; -in -{ - options.my.home.delta = with lib; { - enable = my.mkDisableOption "delta configuration"; - - package = mkPackageOption pkgs "delta" { }; - - git = { - enable = my.mkDisableOption "git integration"; - }; - }; - - config = lib.mkIf cfg.enable { - programs.delta = { - enable = true; - - inherit (cfg) package; - - enableGitIntegration = cfg.git.enable; - - options = { - features = "diff-highlight decorations"; - - # Less jarring style for `diff-highlight` emulation - diff-highlight = { - minus-style = "red"; - minus-non-emph-style = "red"; - minus-emph-style = "bold red 52"; - - plus-style = "green"; - plus-non-emph-style = "green"; - plus-emph-style = "bold green 22"; - - whitespace-error-style = "reverse red"; - }; - - # Personal preference for easier reading - decorations = { - commit-style = "raw"; # Do not recolor meta information - keep-plus-minus-markers = true; - paging = "always"; - }; - }; - }; - }; -} diff --git a/modules/home/direnv/lib/python.sh b/modules/home/direnv/lib/python.sh index b1be8a9..b4b2bce 100644 --- a/modules/home/direnv/lib/python.sh +++ b/modules/home/direnv/lib/python.sh @@ -46,7 +46,7 @@ layout_uv() { fi # create venv if it doesn't exist - uv venv -q --allow-existing + uv venv -q export VIRTUAL_ENV export UV_ACTIVE=1 diff --git a/modules/home/discord/default.nix b/modules/home/discord/default.nix index f9892df..bfa5d40 100644 --- a/modules/home/discord/default.nix +++ b/modules/home/discord/default.nix @@ -1,6 +1,8 @@ { config, lib, pkgs, ... }: let cfg = config.my.home.discord; + + jsonFormat = pkgs.formats.json { }; in { options.my.home.discord = with lib; { @@ -10,15 +12,14 @@ in }; config = lib.mkIf cfg.enable { - programs.discord = { - enable = true; + home.packages = with pkgs; [ + cfg.package + ]; - inherit (cfg) package; - - settings = { + xdg.configFile."discord/settings.json".source = + jsonFormat.generate "discord.json" { # Do not keep me from using the app just to force an update SKIP_HOST_UPDATE = true; }; - }; }; } diff --git a/modules/home/firefox/default.nix b/modules/home/firefox/default.nix index 6346dc9..02c74f2 100644 --- a/modules/home/firefox/default.nix +++ b/modules/home/firefox/default.nix @@ -61,21 +61,19 @@ in "ui.systemUsesDarkTheme" = true; # Dark mode }; - extensions = { - packages = with pkgs.nur.repos.rycee.firefox-addons; ([ - bitwarden - consent-o-matic - form-history-control - reddit-comment-collapser - reddit-enhancement-suite - refined-github - sponsorblock - ublock-origin - ] - ++ lib.optional (cfg.tridactyl.enable) tridactyl - ++ lib.optional (cfg.ff2mpv.enable) ff2mpv - ); - }; + extensions = with pkgs.nur.repos.rycee.firefox-addons; ([ + bitwarden + consent-o-matic + form-history-control + reddit-comment-collapser + reddit-enhancement-suite + refined-github + sponsorblock + ublock-origin + ] + ++ lib.optional (cfg.tridactyl.enable) tridactyl + ++ lib.optional (cfg.ff2mpv.enable) ff2mpv + ); }; }; }; diff --git a/modules/home/firefox/tridactyl/default.nix b/modules/home/firefox/tridactyl/default.nix index 26ddfad..35b58c2 100644 --- a/modules/home/firefox/tridactyl/default.nix +++ b/modules/home/firefox/tridactyl/default.nix @@ -12,7 +12,9 @@ let in { config = lib.mkIf cfg.enable { - xdg.configFile."tridactyl/tridactylrc".source = pkgs.replaceVars ./tridactylrc { + xdg.configFile."tridactyl/tridactylrc".source = pkgs.substituteAll { + src = ./tridactylrc; + editorcmd = lib.concatStringsSep " " [ # Use my configured terminal term diff --git a/modules/home/git/default.nix b/modules/home/git/default.nix index c3a51a0..1bb2215 100644 --- a/modules/home/git/default.nix +++ b/modules/home/git/default.nix @@ -21,31 +21,57 @@ in config.programs.git = lib.mkIf cfg.enable { enable = true; + # Who am I? + userEmail = mkMailAddress "bruno" "belanyi.fr"; + userName = "Bruno BELANYI"; + inherit (cfg) package; + aliases = { + git = "!git"; + lol = "log --graph --decorate --pretty=oneline --abbrev-commit --topo-order"; + lola = "lol --all"; + assume = "update-index --assume-unchanged"; + unassume = "update-index --no-assume-unchanged"; + assumed = "!git ls-files -v | grep ^h | cut -c 3-"; + pick = "log -p -G"; + push-new = "!git push -u origin " + + ''"$(git branch | grep '^* ' | cut -f2- -d' ')"''; + root = "git rev-parse --show-toplevel"; + }; + lfs.enable = true; + delta = { + enable = true; + + options = { + features = "diff-highlight decorations"; + + # Less jarring style for `diff-highlight` emulation + diff-highlight = { + minus-style = "red"; + minus-non-emph-style = "red"; + minus-emph-style = "bold red 52"; + + plus-style = "green"; + plus-non-emph-style = "green"; + plus-emph-style = "bold green 22"; + + whitespace-error-style = "reverse red"; + }; + + # Personal preference for easier reading + decorations = { + commit-style = "raw"; # Do not recolor meta information + keep-plus-minus-markers = true; + paging = "always"; + }; + }; + }; + # There's more - settings = { - # Who am I? - user = { - email = mkMailAddress "bruno" "belanyi.fr"; - name = "Bruno BELANYI"; - }; - - alias = { - git = "!git"; - lol = "log --graph --decorate --pretty=oneline --abbrev-commit --topo-order"; - lola = "lol --all"; - assume = "update-index --assume-unchanged"; - unassume = "update-index --no-assume-unchanged"; - assumed = "!git ls-files -v | grep ^h | cut -c 3-"; - pick = "log -p -G"; - push-new = "!git push -u origin " - + ''"$(git branch | grep '^* ' | cut -f2- -d' ')"''; - root = "git rev-parse --show-toplevel"; - }; - + extraConfig = { # Makes it a bit more readable blame = { coloring = "repeatedLines"; @@ -97,6 +123,11 @@ in defaultBranch = "main"; }; + # Local configuration, not-versioned + include = { + path = "config.local"; + }; + merge = { conflictStyle = "zdiff3"; }; @@ -136,8 +167,8 @@ in }; }; - includes = lib.mkAfter [ - # Multiple identities + # Multiple identities + includes = [ { condition = "gitdir:~/git/EPITA/"; contents = { @@ -156,10 +187,6 @@ in }; }; } - # Local configuration, not-versioned - { - path = "config.local"; - } ]; ignores = diff --git a/modules/home/gpg/default.nix b/modules/home/gpg/default.nix index 2a00baf..51c865a 100644 --- a/modules/home/gpg/default.nix +++ b/modules/home/gpg/default.nix @@ -17,7 +17,7 @@ in services.gpg-agent = { enable = true; enableSshSupport = true; # One agent to rule them all - pinentry.package = cfg.pinentry; + pinentryPackage = cfg.pinentry; extraConfig = '' allow-loopback-pinentry ''; diff --git a/modules/home/jq/default.nix b/modules/home/jq/default.nix index 53e5986..57e266f 100644 --- a/modules/home/jq/default.nix +++ b/modules/home/jq/default.nix @@ -17,7 +17,6 @@ in strings = "0;32"; arrays = "1;39"; objects = "1;39"; - objectKeys = "1;34"; }; }; } diff --git a/modules/home/mail/accounts/default.nix b/modules/home/mail/accounts/default.nix index 5216ad5..202b9bc 100644 --- a/modules/home/mail/accounts/default.nix +++ b/modules/home/mail/accounts/default.nix @@ -26,7 +26,20 @@ let }; migaduConfig = { - flavor = "migadu.com"; + imap = { + host = "imap.migadu.com"; + port = 993; + tls = { + enable = true; + }; + }; + smtp = { + host = "smtp.migadu.com"; + port = 465; + tls = { + enable = true; + }; + }; }; gmailConfig = { diff --git a/modules/home/nix/default.nix b/modules/home/nix/default.nix index 2f435a8..c67cc6a 100644 --- a/modules/home/nix/default.nix +++ b/modules/home/nix/default.nix @@ -69,7 +69,7 @@ in automatic = true; # Every week, with some wiggle room - dates = "weekly"; + frequency = "weekly"; randomizedDelaySec = "10min"; # Use a persistent timer for e.g: laptops diff --git a/modules/home/packages/default.nix b/modules/home/packages/default.nix index 43f7111..1362a06 100644 --- a/modules/home/packages/default.nix +++ b/modules/home/packages/default.nix @@ -1,7 +1,6 @@ -{ config, lib, pkgs, osConfig, ... }: +{ config, lib, pkgs, ... }: let cfg = config.my.home.packages; - useGlobalPkgs = osConfig.home-manager.useGlobalPkgs or false; in { options.my.home.packages = with lib; { @@ -27,10 +26,9 @@ in fd file ripgrep - tree ] ++ cfg.additionalPackages); - nixpkgs.config = lib.mkIf (!useGlobalPkgs) { + nixpkgs.config = { inherit (cfg) allowAliases allowUnfree; }; }; diff --git a/modules/home/pager/default.nix b/modules/home/pager/default.nix index e84dcb7..1119440 100644 --- a/modules/home/pager/default.nix +++ b/modules/home/pager/default.nix @@ -16,11 +16,7 @@ in LESS = "-R -+X -c"; # Better XDG compliance LESSHISTFILE = "${config.xdg.stateHome}/less/history"; + LESSKEY = "${config.xdg.configHome}/less/lesskey"; }; - - xdg.configFile."lesskey".text = '' - # Quit without clearing the screen on `Q` - Q toggle-option -!^Predraw-on-quit\nq - ''; }; } diff --git a/modules/home/secrets/github/token.age b/modules/home/secrets/github/token.age index 3e8bb5a..1d36ccd 100644 Binary files a/modules/home/secrets/github/token.age and b/modules/home/secrets/github/token.age differ diff --git a/modules/home/secrets/secrets.nix b/modules/home/secrets/secrets.nix index 27cdb4e..f474342 100644 --- a/modules/home/secrets/secrets.nix +++ b/modules/home/secrets/secrets.nix @@ -1,6 +1,6 @@ # Common secrets let - keys = import ../../../keys; + keys = import ../../keys; all = builtins.attrValues keys.users; in diff --git a/modules/home/ssh/default.nix b/modules/home/ssh/default.nix index b0b4167..748b195 100644 --- a/modules/home/ssh/default.nix +++ b/modules/home/ssh/default.nix @@ -17,7 +17,6 @@ in { programs.ssh = { enable = true; - enableDefaultConfig = false; includes = [ # Local configuration, not-versioned @@ -54,12 +53,11 @@ in identityFile = "~/.ssh/shared_rsa"; user = "ambroisie"; }; - - # `*` is automatically made the last match block by the module - "*" = { - addKeysToAgent = "yes"; - }; }; + + extraConfig = '' + AddKeysToAgent yes + ''; }; } diff --git a/modules/home/tmux/default.nix b/modules/home/tmux/default.nix index e3e3daf..ca99fdc 100644 --- a/modules/home/tmux/default.nix +++ b/modules/home/tmux/default.nix @@ -6,7 +6,7 @@ let (config.my.home.wm.windowManager != null) ]; - mkTerminalFeature = opt: flag: + mkTerminalFlags = opt: flag: let mkFlag = term: ''set -as terminal-features ",${term}:${flag}"''; enabledTerminals = lib.filterAttrs (_: v: v.${opt}) cfg.terminalFeatures; @@ -20,8 +20,6 @@ in enablePassthrough = mkEnableOption "tmux DCS passthrough sequence"; - enableResurrect = mkEnableOption "tmux-resurrect plugin"; - terminalFeatures = mkOption { type = with types; attrsOf (submodule { options = { @@ -48,21 +46,19 @@ in keyMode = "vi"; # Home-row keys and other niceties clock24 = true; # I'm one of those heathens escapeTime = 0; # Let vim do its thing instead - historyLimit = 1000000; # Bigger buffer + historyLimit = 100000; # Bigger buffer mouse = false; # I dislike mouse support - focusEvents = true; # Report focus events terminal = "tmux-256color"; # I want accurate termcap info - aggressiveResize = true; # Automatic resize when switching client size - plugins = with pkgs.tmuxPlugins; builtins.filter (attr: attr != { }) [ + plugins = with pkgs.tmuxPlugins; [ # Open high-lighted files in copy mode open # Better pane management pain-control # Better session management sessionist - # X clipboard integration { + # X clipboard integration plugin = yank; extraConfig = '' # Use 'clipboard' because of misbehaving apps (e.g: firefox) @@ -71,8 +67,8 @@ in set -g @yank_action 'copy-pipe' ''; } - # Show when prefix has been pressed { + # Show when prefix has been pressed plugin = prefix-highlight; extraConfig = '' # Also show when I'm in copy or sync mode @@ -82,13 +78,6 @@ in set -g status-right '#{prefix_highlight} %a %Y-%m-%d %H:%M' ''; } - # Resurrect sessions - (lib.optionalAttrs cfg.enableResurrect { - plugin = resurrect; - extraConfig = '' - set -g @resurrect-dir '${config.xdg.stateHome}/tmux/resurrect' - ''; - }) ]; extraConfig = '' @@ -97,7 +86,7 @@ in # Accept sloppy Ctrl key when switching windows, on top of default mapping bind-key -N "Select the previous window" C-p previous-window - bind-key -N "Select the next window" C-n next-window + bind-key -N "Select the next window" C-n next -window # Better vim mode bind-key -T copy-mode-vi 'v' send -X begin-selection @@ -123,9 +112,9 @@ in } # Force OSC8 hyperlinks for each relevant $TERM - ${mkTerminalFeature "hyperlinks" "hyperlinks"} + ${mkTerminalFlags "hyperlinks" "hyperlinks"} # Force 24-bit color for each relevant $TERM - ${mkTerminalFeature "trueColor" "RGB"} + ${mkTerminalFlags "trueColor" "RGB"} ''; }; } diff --git a/modules/home/trgui/default.nix b/modules/home/trgui/default.nix deleted file mode 100644 index ee545a9..0000000 --- a/modules/home/trgui/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ config, lib, pkgs, ... }: -let - cfg = config.my.home.trgui; -in -{ - options.my.home.trgui = with lib; { - enable = mkEnableOption "Transmission GUI onfiguration"; - - package = mkPackageOption pkgs "TrguiNG" { default = "trgui-ng"; }; - }; - - config = lib.mkIf cfg.enable { - home.packages = with pkgs; [ - cfg.package - ]; - }; -} diff --git a/modules/home/vim/after/ftplugin/query.vim b/modules/home/vim/after/ftplugin/query.vim deleted file mode 100644 index fd2ac73..0000000 --- a/modules/home/vim/after/ftplugin/query.vim +++ /dev/null @@ -1,6 +0,0 @@ -" Create the `b:undo_ftplugin` variable if it doesn't exist -call ftplugined#check_undo_ft() - -" Use a small indentation value on query files -setlocal shiftwidth=2 -let b:undo_ftplugin.='|setlocal shiftwidth<' diff --git a/modules/home/vim/after/plugin/mappings/unimpaired.lua b/modules/home/vim/after/plugin/mappings/unimpaired.lua index 765b6b1..82aab05 100644 --- a/modules/home/vim/after/plugin/mappings/unimpaired.lua +++ b/modules/home/vim/after/plugin/mappings/unimpaired.lua @@ -31,6 +31,8 @@ local keys = { { "[u", desc = "URL encode" }, { "[x", desc = "XML encode" }, { "[y", desc = "C string encode" }, + -- Custom + { "[d", lsp.goto_prev_diagnostic, desc = "Previous diagnostic" }, -- Next { "]", group = "Next" }, @@ -60,6 +62,8 @@ local keys = { { "]u", desc = "URL decode" }, { "]x", desc = "XML decode" }, { "]y", desc = "C string decode" }, + -- Custom + { "]d", lsp.goto_next_diagnostic, desc = "Next diagnostic" }, -- Enable option { "[o", group = "Enable option" }, diff --git a/modules/home/vim/after/queries/gitcommit/highlights.scm b/modules/home/vim/after/queries/gitcommit/highlights.scm deleted file mode 100644 index 05162c9..0000000 --- a/modules/home/vim/after/queries/gitcommit/highlights.scm +++ /dev/null @@ -1,6 +0,0 @@ -; extends - -; Highlight over-extended subject lines (rely on wrapping for message body) -((subject) @comment.error - (#vim-match? @comment.error ".\{50,}") - (#offset! @comment.error 0 50 0 0)) diff --git a/modules/home/vim/default.nix b/modules/home/vim/default.nix index 930a853..8e6bd5c 100644 --- a/modules/home/vim/default.nix +++ b/modules/home/vim/default.nix @@ -59,6 +59,7 @@ in # LSP and linting nvim-lspconfig # Easy LSP configuration lsp-format-nvim # Simplified formatting configuration + lsp_lines-nvim # Show diagnostics *over* regions none-ls-nvim # LSP integration for linters and formatters nvim-treesitter.withAllGrammars # Better highlighting nvim-treesitter-textobjects # More textobjects @@ -66,6 +67,7 @@ in # Completion luasnip # Snippet manager compatible with LSP + friendly-snippets # LSP snippets collection nvim-cmp # Completion engine cmp-async-path # More responsive path completion cmp-buffer # Words from open buffers @@ -80,6 +82,7 @@ in nvim-surround # Deal with pairs, now in Lua oil-nvim # Better alternative to NetrW telescope-fzf-native-nvim # Use 'fzf' fuzzy matching algorithm + telescope-lsp-handlers-nvim # Use 'telescope' for various LSP actions telescope-nvim # Fuzzy finder interface which-key-nvim # Show available mappings ]; diff --git a/modules/home/vim/ftdetect/glsl.lua b/modules/home/vim/ftdetect/glsl.lua new file mode 100644 index 0000000..2f4f1dd --- /dev/null +++ b/modules/home/vim/ftdetect/glsl.lua @@ -0,0 +1,7 @@ +-- Use GLSL filetype for common shader file extensions +vim.filetype.add({ + extension = { + frag = "glsl", + vert = "glsl", + }, +}) diff --git a/modules/home/vim/init.vim b/modules/home/vim/init.vim index 1142925..0b54676 100644 --- a/modules/home/vim/init.vim +++ b/modules/home/vim/init.vim @@ -68,6 +68,8 @@ set listchars=tab:>─,trail:·,nbsp:¤ " Use patience diff set diffopt+=algorithm:patience +" Align similar lines in each hunk +set diffopt+=linematch:50 " Don't redraw when executing macros set lazyredraw @@ -81,6 +83,9 @@ set updatetime=250 " Disable all mouse integrations set mouse= +" Set dark mode by default +set background=dark + " Setup some overrides for gruvbox lua << EOF local gruvbox = require("gruvbox") diff --git a/modules/home/vim/lua/ambroisie/lsp.lua b/modules/home/vim/lua/ambroisie/lsp.lua index fef0487..eb53da6 100644 --- a/modules/home/vim/lua/ambroisie/lsp.lua +++ b/modules/home/vim/lua/ambroisie/lsp.lua @@ -3,6 +3,43 @@ local M = {} -- Simplified LSP formatting configuration local lsp_format = require("lsp-format") +--- Move to the next/previous diagnostic, automatically showing the diagnostics +--- float if necessary. +--- @param forward bool whether to go forward or backwards +local function goto_diagnostic(forward) + vim.validate({ + forward = { forward, "boolean" }, + }) + + local opts = { + float = false, + } + + -- Only show floating diagnostics if they are otherwise not displayed + local config = vim.diagnostic.config() + if not (config.virtual_text or config.virtual_lines) then + opts.float = true + end + + if forward then + vim.diagnostic.goto_next(opts) + else + vim.diagnostic.goto_prev(opts) + end +end + +--- Move to the next diagnostic, automatically showing the diagnostics float if +--- necessary. +M.goto_next_diagnostic = function() + goto_diagnostic(true) +end + +--- Move to the previous diagnostic, automatically showing the diagnostics float +--- if necessary. +M.goto_prev_diagnostic = function() + goto_diagnostic(false) +end + --- shared LSP configuration callback --- @param client native client configuration --- @param bufnr int? buffer number of the attached client @@ -42,10 +79,6 @@ M.on_attach = function(client, bufnr) vim.diagnostic.config({ virtual_text = text, virtual_lines = lines, - jump = { - -- Show float on jump if no diagnostic text is otherwise shown - float = not (text or lines), - }, }) end @@ -53,10 +86,6 @@ M.on_attach = function(client, bufnr) vim.diagnostic.open_float(nil, { scope = "buffer" }) end - local function toggle_inlay_hints() - vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled()) - end - local keys = { buffer = bufnr, -- LSP navigation @@ -71,7 +100,6 @@ M.on_attach = function(client, bufnr) { "ca", vim.lsp.buf.code_action, desc = "Code actions" }, { "cd", cycle_diagnostics_display, desc = "Cycle diagnostics display" }, { "cD", show_buffer_diagnostics, desc = "Show buffer diagnostics" }, - { "ch", toggle_inlay_hints, desc = "Toggle inlay hints" }, { "cr", vim.lsp.buf.rename, desc = "Rename symbol" }, { "cs", vim.lsp.buf.signature_help, desc = "Show signature" }, { "ct", vim.lsp.buf.type_definition, desc = "Go to type definition" }, diff --git a/modules/home/vim/lua/ambroisie/utils.lua b/modules/home/vim/lua/ambroisie/utils.lua index 0ee7c83..c9e9292 100644 --- a/modules/home/vim/lua/ambroisie/utils.lua +++ b/modules/home/vim/lua/ambroisie/utils.lua @@ -38,7 +38,7 @@ end --- @param bufnr int? buffer number --- @return table all active LSP client names M.list_lsp_clients = function(bufnr) - local clients = vim.lsp.get_clients({ bufnr = bufnr }) + local clients = vim.lsp.get_active_clients({ bufnr = bufnr }) local names = {} for _, client in ipairs(clients) do diff --git a/modules/home/vim/plugin/numbertoggle.lua b/modules/home/vim/plugin/numbertoggle.lua index b1e3df2..8042710 100644 --- a/modules/home/vim/plugin/numbertoggle.lua +++ b/modules/home/vim/plugin/numbertoggle.lua @@ -22,3 +22,13 @@ vim.api.nvim_create_autocmd({ "BufLeave", "FocusLost", "InsertEnter", "WinLeave" end end, }) + +-- Never show the sign column in a terminal buffer +vim.api.nvim_create_autocmd({ "TermOpen" }, { + pattern = "*", + group = numbertoggle, + callback = function() + vim.opt.number = false + vim.opt.relativenumber = false + end, +}) diff --git a/modules/home/vim/plugin/settings/fastfold.lua b/modules/home/vim/plugin/settings/fastfold.lua new file mode 100644 index 0000000..78ee937 --- /dev/null +++ b/modules/home/vim/plugin/settings/fastfold.lua @@ -0,0 +1,5 @@ +-- Intercept all fold commands +-- stylua: ignore +vim.g.fastfold_fold_command_suffixes = { + "x", "X", "a", "A", "o", "O", "c", "C", "r", "R", "m", "M", "i", "n", "N", +} diff --git a/modules/home/vim/plugin/settings/lsp-lines.lua b/modules/home/vim/plugin/settings/lsp-lines.lua new file mode 100644 index 0000000..9c79818 --- /dev/null +++ b/modules/home/vim/plugin/settings/lsp-lines.lua @@ -0,0 +1,3 @@ +local lsp_lines = require("lsp_lines") + +lsp_lines.setup() diff --git a/modules/home/vim/plugin/settings/lspconfig.lua b/modules/home/vim/plugin/settings/lspconfig.lua index 1596e84..9e9425c 100644 --- a/modules/home/vim/plugin/settings/lspconfig.lua +++ b/modules/home/vim/plugin/settings/lspconfig.lua @@ -1,3 +1,4 @@ +local lspconfig = require("lspconfig") local lsp = require("ambroisie.lsp") local utils = require("ambroisie.utils") @@ -15,57 +16,79 @@ vim.diagnostic.config({ update_in_insert = false, -- Show highest severity first severity_sort = true, - jump = { - -- Show float on diagnostic jumps - float = true, - }, }) -- Inform servers we are able to do completion, snippets, etc... local capabilities = require("cmp_nvim_lsp").default_capabilities() --- Shared configuration -vim.lsp.config("*", { - capabilities = capabilities, - on_attach = lsp.on_attach, -}) - -local servers = { - -- C/C++ - clangd = {}, - -- Haskell - hls = {}, - -- Nix - nil_ls = {}, - -- Python - pyright = {}, - ruff = {}, - -- Rust - rust_analyzer = {}, - -- Shell - bashls = { - filetypes = { "bash", "sh", "zsh" }, - settings = { - bashIde = { - shfmt = { - -- Simplify the code - simplifyCode = true, - -- Indent switch cases - caseIndent = true, - }, - }, - }, - }, - -- Starlark - starpls = {}, - -- Generic - harper_ls = {}, - typos_lsp = {}, -} - -for server, config in pairs(servers) do - if not vim.tbl_isempty(config) then - vim.lsp.config(server, config) - end - vim.lsp.enable(server) +-- C/C++ +if utils.is_executable("clangd") then + lspconfig.clangd.setup({ + capabilities = capabilities, + on_attach = lsp.on_attach, + }) +end + +-- Haskell +if utils.is_executable("haskell-language-server-wrapper") then + lspconfig.hls.setup({ + capabilities = capabilities, + on_attach = lsp.on_attach, + }) +end + +-- Nix +if utils.is_executable("nil") then + lspconfig.nil_ls.setup({ + capabilities = capabilities, + on_attach = lsp.on_attach, + }) +end + +-- Python +if utils.is_executable("pyright") then + lspconfig.pyright.setup({ + capabilities = capabilities, + on_attach = lsp.on_attach, + }) +end + +if utils.is_executable("ruff") then + lspconfig.ruff.setup({ + capabilities = capabilities, + on_attach = lsp.on_attach, + }) +end + +-- Rust +if utils.is_executable("rust-analyzer") then + lspconfig.rust_analyzer.setup({ + capabilities = capabilities, + on_attach = lsp.on_attach, + }) +end + +-- Shell +if utils.is_executable("bash-language-server") then + lspconfig.bashls.setup({ + filetypes = { "bash", "sh", "zsh" }, + capabilities = capabilities, + on_attach = lsp.on_attach, + }) +end + +-- Starlark +if utils.is_executable("starpls") then + lspconfig.starpls.setup({ + capabilities = capabilities, + on_attach = lsp.on_attach, + }) +end + +-- Generic +if utils.is_executable("typos-lsp") then + lspconfig.typos_lsp.setup({ + capabilities = capabilities, + on_attach = lsp.on_attach, + }) end diff --git a/modules/home/vim/plugin/settings/lualine.lua b/modules/home/vim/plugin/settings/lualine.lua index bbe4647..5219a95 100644 --- a/modules/home/vim/plugin/settings/lualine.lua +++ b/modules/home/vim/plugin/settings/lualine.lua @@ -1,5 +1,4 @@ local lualine = require("lualine") -local oil = require("oil") local utils = require("ambroisie.utils") local function list_spell_languages() @@ -31,7 +30,7 @@ lualine.setup({ { "mode" }, }, lualine_b = { - { "branch" }, + { "FugitiveHead" }, { "filename", symbols = { readonly = "🔒" } }, }, lualine_c = { @@ -58,21 +57,5 @@ lualine.setup({ extensions = { "fugitive", "quickfix", - { - sections = { - lualine_a = { - { "mode" }, - }, - lualine_b = { - { "branch" }, - }, - lualine_c = { - function() - return vim.fn.fnamemodify(oil.get_current_dir(), ":~") - end, - }, - }, - filetypes = { "oil" }, - }, }, }) diff --git a/modules/home/vim/plugin/settings/luasnip.lua b/modules/home/vim/plugin/settings/luasnip.lua new file mode 100644 index 0000000..80309d7 --- /dev/null +++ b/modules/home/vim/plugin/settings/luasnip.lua @@ -0,0 +1 @@ +require("luasnip.loaders.from_vscode").lazy_load() diff --git a/modules/home/vim/plugin/settings/null-ls.lua b/modules/home/vim/plugin/settings/null-ls.lua index 258a209..eadf16a 100644 --- a/modules/home/vim/plugin/settings/null-ls.lua +++ b/modules/home/vim/plugin/settings/null-ls.lua @@ -46,3 +46,29 @@ null_ls.register({ condition = utils.is_executable_condition("isort"), }), }) + +-- Shell (non-POSIX) +null_ls.register({ + null_ls.builtins.formatting.shfmt.with({ + -- Indent with 4 spaces, simplify the code, indent switch cases, + -- add space after redirection, use bash dialect + extra_args = { "-i", "4", "-s", "-ci", "-sr", "-ln", "bash" }, + -- Restrict to bash and zsh + filetypes = { "bash", "zsh" }, + -- Only used if available + condition = utils.is_executable_condition("shfmt"), + }), +}) + +-- Shell (POSIX) +null_ls.register({ + null_ls.builtins.formatting.shfmt.with({ + -- Indent with 4 spaces, simplify the code, indent switch cases, + -- add space after redirection, use POSIX + extra_args = { "-i", "4", "-s", "-ci", "-sr", "-ln", "posix" }, + -- Restrict to POSIX sh + filetypes = { "sh" }, + -- Only used if available + condition = utils.is_executable_condition("shfmt"), + }), +}) diff --git a/modules/home/vim/plugin/settings/oil.lua b/modules/home/vim/plugin/settings/oil.lua index 74d5007..a160725 100644 --- a/modules/home/vim/plugin/settings/oil.lua +++ b/modules/home/vim/plugin/settings/oil.lua @@ -4,8 +4,6 @@ local wk = require("which-key") local detail = false oil.setup({ - -- Don't show icons - columns = {}, view_options = { -- Show files and directories that start with "." by default show_hidden = true, diff --git a/modules/home/vim/plugin/settings/telescope.lua b/modules/home/vim/plugin/settings/telescope.lua index 810d51c..1a23928 100644 --- a/modules/home/vim/plugin/settings/telescope.lua +++ b/modules/home/vim/plugin/settings/telescope.lua @@ -23,6 +23,7 @@ telescope.setup({ }) telescope.load_extension("fzf") +telescope.load_extension("lsp_handlers") local keys = { { "f", group = "Fuzzy finder" }, diff --git a/modules/home/vim/plugin/signtoggle.lua b/modules/home/vim/plugin/signtoggle.lua index 3deca34..9765a81 100644 --- a/modules/home/vim/plugin/signtoggle.lua +++ b/modules/home/vim/plugin/signtoggle.lua @@ -1,21 +1,26 @@ local signtoggle = vim.api.nvim_create_augroup("signtoggle", { clear = true }) --- Only show sign column for the currently focused buffer, if it has a number column +-- Only show sign column for the currently focused buffer vim.api.nvim_create_autocmd({ "BufEnter", "FocusGained", "WinEnter" }, { pattern = "*", group = signtoggle, callback = function() - if vim.opt.number:get() then - vim.opt.signcolumn = "yes" - end + vim.opt.signcolumn = "yes" end, }) vim.api.nvim_create_autocmd({ "BufLeave", "FocusLost", "WinLeave" }, { pattern = "*", group = signtoggle, callback = function() - if vim.opt.number:get() then - vim.opt.signcolumn = "no" - end + vim.opt.signcolumn = "no" + end, +}) + +-- Never show the sign column in a terminal buffer +vim.api.nvim_create_autocmd({ "TermOpen" }, { + pattern = "*", + group = signtoggle, + callback = function() + vim.opt.signcolumn = "no" end, }) diff --git a/modules/home/wm/i3/default.nix b/modules/home/wm/i3/default.nix index 5f22bbe..c432864 100644 --- a/modules/home/wm/i3/default.nix +++ b/modules/home/wm/i3/default.nix @@ -127,11 +127,9 @@ in { class = "^Blueman-.*$"; } { title = "^htop$"; } { class = "^Thunderbird$"; instance = "Mailnews"; window_role = "filterlist"; } - { class = "^firefox$"; instance = "Places"; window_role = "Organizer"; } - { class = "^pavucontrol.*$"; } + { class = "^Pavucontrol.*$"; } { class = "^Arandr$"; } - { class = "^\\.blueman-manager-wrapped$"; } - { class = "^\\.arandr-wrapped$"; } + { class = ".?blueman-manager.*$"; } ]; }; diff --git a/modules/home/xdg/default.nix b/modules/home/xdg/default.nix index 7a0c517..803167f 100644 --- a/modules/home/xdg/default.nix +++ b/modules/home/xdg/default.nix @@ -56,7 +56,4 @@ in XCOMPOSECACHE = "${dataHome}/X11/xcompose"; _JAVA_OPTIONS = "-Djava.util.prefs.userRoot=${configHome}/java"; }; - - # Some modules *optionally* use `XDG_*_HOME` when told to - config.home.preferXdgDirectories = lib.mkIf cfg.enable true; } diff --git a/modules/home/zsh/default.nix b/modules/home/zsh/default.nix index 9524262..11b6cb2 100644 --- a/modules/home/zsh/default.nix +++ b/modules/home/zsh/default.nix @@ -1,6 +1,14 @@ { config, pkgs, lib, ... }: let cfg = config.my.home.zsh; + + # Have a nice relative path for XDG_CONFIG_HOME, without leading `/` + relativeXdgConfig = + let + noHome = lib.removePrefix config.home.homeDirectory; + noSlash = lib.removePrefix "/"; + in + noSlash (noHome config.xdg.configHome); in { options.my.home.zsh = with lib; { @@ -8,22 +16,16 @@ in launchTmux = mkEnableOption "auto launch tmux at shell start"; - completionSync = { - enable = mkEnableOption "zsh-completion-sync plugin"; - }; - notify = { enable = mkEnableOption "zsh-done notification"; exclude = mkOption { type = with types; listOf str; default = [ - "bat" "delta" "direnv reload" "fg" "git (?!push|pull|fetch)" - "home-manager (?!switch|build)" "htop" "less" "man" @@ -55,7 +57,7 @@ in programs.zsh = { enable = true; - dotDir = "${config.xdg.configHome}/zsh"; # Don't clutter $HOME + dotDir = "${relativeXdgConfig}/zsh"; # Don't clutter $HOME enableCompletion = true; history = { @@ -72,7 +74,7 @@ in plugins = [ { name = "fast-syntax-highlighting"; - file = "share/zsh/plugins/fast-syntax-highlighting/fast-syntax-highlighting.plugin.zsh"; + file = "share/zsh/site-functions/fast-syntax-highlighting.plugin.zsh"; src = pkgs.zsh-fast-syntax-highlighting; } { @@ -85,26 +87,28 @@ in # Modal editing is life, but CLI benefits from emacs gymnastics defaultKeymap = "emacs"; - initContent = lib.mkMerge [ - # Make those happen early to avoid doing double the work - (lib.mkBefore (lib.optionalString cfg.launchTmux '' - # Launch tmux unless already inside one - if [ -z "$TMUX" ]; then - exec tmux new-session - fi - '')) + # Make those happen early to avoid doing double the work + initExtraFirst = lib.mkBefore '' + ${ + lib.optionalString cfg.launchTmux '' + # Launch tmux unless already inside one + if [ -z "$TMUX" ]; then + exec tmux new-session + fi + '' + } + ''; - (lib.mkAfter '' - source ${./completion-styles.zsh} - source ${./extra-mappings.zsh} - source ${./options.zsh} + initExtra = lib.mkAfter '' + source ${./completion-styles.zsh} + source ${./extra-mappings.zsh} + source ${./options.zsh} - # Source local configuration - if [ -f "$ZDOTDIR/zshrc.local" ]; then - source "$ZDOTDIR/zshrc.local" - fi - '') - ]; + # Source local configuration + if [ -f "$ZDOTDIR/zshrc.local" ]; then + source "$ZDOTDIR/zshrc.local" + fi + ''; localVariables = { # I like having the full path @@ -122,18 +126,6 @@ in }; } - (lib.mkIf cfg.completionSync.enable { - programs.zsh = { - plugins = [ - { - name = "zsh-completion-sync"; - file = "share/zsh-completion-sync/zsh-completion-sync.plugin.zsh"; - src = pkgs.zsh-completion-sync; - } - ]; - }; - }) - (lib.mkIf cfg.notify.enable { programs.zsh = { plugins = [ @@ -159,7 +151,7 @@ in }; # Use OSC-777 to send the notification through SSH - initContent = lib.mkIf cfg.notify.ssh.useOsc777 '' + initExtra = lib.mkIf cfg.notify.ssh.useOsc777 '' done_send_notification() { local exit_status="$1" local title="$2" diff --git a/modules/nixos/hardware/bluetooth/default.nix b/modules/nixos/hardware/bluetooth/default.nix index b14ac21..efce5c4 100644 --- a/modules/nixos/hardware/bluetooth/default.nix +++ b/modules/nixos/hardware/bluetooth/default.nix @@ -18,9 +18,16 @@ in services.blueman.enable = true; } + # Persist bluetooth files + { + my.system.persist.directories = [ + "/var/lib/bluetooth" + ]; + } + # Support for additional bluetooth codecs (lib.mkIf cfg.loadExtraCodecs { - services.pulseaudio = { + hardware.pulseaudio = { extraModules = [ pkgs.pulseaudio-modules-bt ]; package = pkgs.pulseaudioFull; }; diff --git a/modules/nixos/hardware/graphics/default.nix b/modules/nixos/hardware/graphics/default.nix index 4b6eb37..89bb1cd 100644 --- a/modules/nixos/hardware/graphics/default.nix +++ b/modules/nixos/hardware/graphics/default.nix @@ -15,6 +15,8 @@ in amd = { enableKernelModule = lib.my.mkDisableOption "Kernel driver module"; + + amdvlk = lib.mkEnableOption "Use AMDVLK instead of Mesa RADV driver"; }; intel = { @@ -31,8 +33,16 @@ in # AMD GPU (lib.mkIf (cfg.gpuFlavor == "amd") { + boot.initrd.kernelModules = lib.mkIf cfg.amd.enableKernelModule [ "amdgpu" ]; + hardware.amdgpu = { - initrd.enable = cfg.amd.enableKernelModule; + # Vulkan + amdvlk = lib.mkIf cfg.amd.amdvlk { + enable = true; + support32Bit = { + enable = true; + }; + }; }; hardware.graphics = { diff --git a/modules/nixos/hardware/networking/default.nix b/modules/nixos/hardware/networking/default.nix index f0806fe..9e85966 100644 --- a/modules/nixos/hardware/networking/default.nix +++ b/modules/nixos/hardware/networking/default.nix @@ -22,6 +22,18 @@ in config = lib.mkMerge [ (lib.mkIf cfg.wireless.enable { networking.networkmanager.enable = true; + + # IWD needs persistence if enabled + + # Persist NetworkManager files + my.system.persist.files = [ + "/var/lib/NetworkManager/secret_key" + "/var/lib/NetworkManager/seen-bssids" + "/var/lib/NetworkManager/timestamps" + ]; + my.system.persist.directories = [ + "/etc/NetworkManager/system-connections" + ]; }) ]; } diff --git a/modules/nixos/hardware/sound/default.nix b/modules/nixos/hardware/sound/default.nix index cd453de..e8ba7f7 100644 --- a/modules/nixos/hardware/sound/default.nix +++ b/modules/nixos/hardware/sound/default.nix @@ -54,7 +54,10 @@ in # Pulseaudio setup (lib.mkIf cfg.pulse.enable { - services.pulseaudio.enable = true; + # ALSA + sound.enable = true; + + hardware.pulseaudio.enable = true; }) ]); } diff --git a/modules/nixos/profiles/wm/default.nix b/modules/nixos/profiles/wm/default.nix index bca4d70..c227328 100644 --- a/modules/nixos/profiles/wm/default.nix +++ b/modules/nixos/profiles/wm/default.nix @@ -24,8 +24,6 @@ in my.home.udiskie.enable = true; # udiskie fails if it can't find this dbus service services.udisks2.enable = true; - # Ensure i3lock can actually unlock the session - security.pam.services.i3lock.enable = true; }) ]; } diff --git a/modules/nixos/profiles/x/default.nix b/modules/nixos/profiles/x/default.nix index 874f36f..ea77939 100644 --- a/modules/nixos/profiles/x/default.nix +++ b/modules/nixos/profiles/x/default.nix @@ -13,7 +13,7 @@ in # Nice wallpaper services.xserver.displayManager.lightdm.background = let - wallpapers = "${pkgs.kdePackages.plasma-workspace-wallpapers}/share/wallpapers"; + wallpapers = "${pkgs.plasma5Packages.plasma-workspace-wallpapers}/share/wallpapers"; in "${wallpapers}/summer_1am/contents/images/2560x1600.jpg"; diff --git a/modules/nixos/services/aria/default.nix b/modules/nixos/services/aria/default.nix index acbf0b7..be35909 100644 --- a/modules/nixos/services/aria/default.nix +++ b/modules/nixos/services/aria/default.nix @@ -69,6 +69,11 @@ in }; }; + my.system.persist.directories = [ + cfg.downloadDir + "/var/lib/aria2" + ]; + # NOTE: unfortunately aria2 does not log connection failures for fail2ban }; } diff --git a/modules/nixos/services/audiobookshelf/default.nix b/modules/nixos/services/audiobookshelf/default.nix index 04ec8b9..5e79990 100644 --- a/modules/nixos/services/audiobookshelf/default.nix +++ b/modules/nixos/services/audiobookshelf/default.nix @@ -34,6 +34,10 @@ in }; }; + my.system.persist.directories = [ + "/var/lib/${config.services.audiobookshelf.dataDir}" + ]; + services.fail2ban.jails = { audiobookshelf = '' enabled = true diff --git a/modules/nixos/services/blog/default.nix b/modules/nixos/services/blog/default.nix index e4d2d42..aadc4f0 100644 --- a/modules/nixos/services/blog/default.nix +++ b/modules/nixos/services/blog/default.nix @@ -41,5 +41,12 @@ in # Those are all subdomains, no problem my.services.nginx.virtualHosts = hostsInfo; + + my.system.persist.directories = [ + "/var/www/blog" + "/var/www/cv" + "/var/www/dev" + "/var/www/key" + ]; }; } diff --git a/modules/nixos/services/calibre-web/default.nix b/modules/nixos/services/calibre-web/default.nix index b7bf9df..8a8af33 100644 --- a/modules/nixos/services/calibre-web/default.nix +++ b/modules/nixos/services/calibre-web/default.nix @@ -53,6 +53,11 @@ in ]; }; + my.system.persist.directories = [ + "/var/lib/${config.services.calibre-web.dataDir}" + cfg.libraryPath + ]; + services.fail2ban.jails = { calibre-web = '' enabled = true diff --git a/modules/nixos/services/default.nix b/modules/nixos/services/default.nix index e03eca1..651f3f8 100644 --- a/modules/nixos/services/default.nix +++ b/modules/nixos/services/default.nix @@ -14,7 +14,7 @@ ./forgejo ./gitea ./grocy - ./homebox + ./indexers ./jellyfin ./komga ./lohr @@ -38,7 +38,6 @@ ./servarr ./ssh-server ./tandoor-recipes - ./thelounge ./tlp ./transmission ./vikunja diff --git a/modules/nixos/services/drone/runner-docker/default.nix b/modules/nixos/services/drone/runner-docker/default.nix index e53c608..1db263b 100644 --- a/modules/nixos/services/drone/runner-docker/default.nix +++ b/modules/nixos/services/drone/runner-docker/default.nix @@ -39,5 +39,7 @@ in extraGroups = [ "docker" ]; # Give access to the daemon }; users.groups.drone-runner-docker = { }; + + # FIXME: persistence? }; } diff --git a/modules/nixos/services/drone/runner-exec/default.nix b/modules/nixos/services/drone/runner-exec/default.nix index a9bb563..c30a1a2 100644 --- a/modules/nixos/services/drone/runner-exec/default.nix +++ b/modules/nixos/services/drone/runner-exec/default.nix @@ -63,5 +63,7 @@ in group = "drone-runner-exec"; }; users.groups.drone-runner-exec = { }; + + # FIXME: persistence? }; } diff --git a/modules/nixos/services/drone/server/default.nix b/modules/nixos/services/drone/server/default.nix index d6148f4..b5d5df7 100644 --- a/modules/nixos/services/drone/server/default.nix +++ b/modules/nixos/services/drone/server/default.nix @@ -6,8 +6,8 @@ in config = lib.mkIf cfg.enable { systemd.services.drone-server = { wantedBy = [ "multi-user.target" ]; - after = [ "postgresql.target" ]; - requires = [ "postgresql.target" ]; + after = [ "postgresql.service" ]; + requires = [ "postgresql.service" ]; serviceConfig = { EnvironmentFile = [ cfg.secretFile @@ -50,5 +50,7 @@ in inherit (cfg) port; }; }; + + # FIXME: persistence? }; } diff --git a/modules/nixos/services/fail2ban/default.nix b/modules/nixos/services/fail2ban/default.nix index be5f7da..8dcc80d 100644 --- a/modules/nixos/services/fail2ban/default.nix +++ b/modules/nixos/services/fail2ban/default.nix @@ -33,5 +33,9 @@ in bantime = "10m"; }; }; + + my.system.persist.directories = [ + "/var/lib/fail2ban" + ]; }; } diff --git a/modules/nixos/services/flood/default.nix b/modules/nixos/services/flood/default.nix index f3fe90b..b129617 100644 --- a/modules/nixos/services/flood/default.nix +++ b/modules/nixos/services/flood/default.nix @@ -28,6 +28,10 @@ in }; }; + my.system.persist.directories = [ + "/var/lib/flood" + ]; + # NOTE: unfortunately flood does not log connection failures for fail2ban }; } diff --git a/modules/nixos/services/forgejo/default.nix b/modules/nixos/services/forgejo/default.nix index 511724b..b7cc0c5 100644 --- a/modules/nixos/services/forgejo/default.nix +++ b/modules/nixos/services/forgejo/default.nix @@ -147,6 +147,11 @@ in ]; }; + my.system.persist.directories = [ + config.services.forgejo.lfs.contentDir + config.services.forgejo.repositoryRoot + ]; + services.fail2ban.jails = { forgejo = '' enabled = true diff --git a/modules/nixos/services/gitea/default.nix b/modules/nixos/services/gitea/default.nix index 95bdf42..76de5dd 100644 --- a/modules/nixos/services/gitea/default.nix +++ b/modules/nixos/services/gitea/default.nix @@ -131,6 +131,11 @@ in ]; }; + my.system.persist.directories = [ + config.services.gitea.lfs.contentDir + config.services.gitea.repositoryRoot + ]; + services.fail2ban.jails = { gitea = '' enabled = true diff --git a/modules/nixos/services/grocy/default.nix b/modules/nixos/services/grocy/default.nix index 9045b03..57295f1 100644 --- a/modules/nixos/services/grocy/default.nix +++ b/modules/nixos/services/grocy/default.nix @@ -37,6 +37,16 @@ in useACMEHost = config.networking.domain; }; + my.services.backup = { + paths = [ + config.services.grocy.dataDir + ]; + }; + + my.system.persist.directories = [ + config.services.grocy.dataDir + ]; + # NOTE: unfortunately grocy does not log connection failures for fail2ban }; } diff --git a/modules/nixos/services/homebox/default.nix b/modules/nixos/services/homebox/default.nix deleted file mode 100644 index 524a6d7..0000000 --- a/modules/nixos/services/homebox/default.nix +++ /dev/null @@ -1,48 +0,0 @@ -# Home inventory made easy -{ config, lib, ... }: -let - cfg = config.my.services.homebox; -in -{ - options.my.services.homebox = with lib; { - enable = mkEnableOption "Homebox home inventory"; - - port = mkOption { - type = types.port; - default = 7745; - example = 8080; - description = "Internal port for webui"; - }; - }; - - config = lib.mkIf cfg.enable { - services.homebox = { - enable = true; - - # Automatic PostgreSQL provisioning - database = { - createLocally = true; - }; - - settings = { - # FIXME: mailer? - HBOX_WEB_PORT = toString cfg.port; - }; - }; - - my.services.nginx.virtualHosts = { - homebox = { - inherit (cfg) port; - websocketsLocations = [ "/api" ]; - }; - }; - - my.services.backup = { - paths = [ - (lib.removePrefix "file://" config.services.homebox.settings.HBOX_STORAGE_CONN_STRING) - ]; - }; - - # NOTE: unfortunately homebox does not log connection failures for fail2ban - }; -} diff --git a/modules/nixos/services/indexers/default.nix b/modules/nixos/services/indexers/default.nix new file mode 100644 index 0000000..58f845d --- /dev/null +++ b/modules/nixos/services/indexers/default.nix @@ -0,0 +1,90 @@ +# Torrent and usenet meta-indexers +{ config, lib, ... }: +let + cfg = config.my.services.indexers; + + jackettPort = 9117; + nzbhydraPort = 5076; + prowlarrPort = 9696; +in +{ + options.my.services.indexers = with lib; { + jackett.enable = mkEnableOption "Jackett torrent meta-indexer"; + nzbhydra.enable = mkEnableOption "NZBHydra2 usenet meta-indexer"; + prowlarr.enable = mkEnableOption "Prowlarr torrent & usenet meta-indexer"; + }; + + config = lib.mkMerge [ + (lib.mkIf cfg.jackett.enable { + services.jackett = { + enable = true; + }; + + # Jackett wants to eat *all* my RAM if left to its own devices + systemd.services.jackett = { + serviceConfig = { + MemoryHigh = "15%"; + MemoryMax = "25%"; + }; + }; + + my.services.nginx.virtualHosts = { + jackett = { + port = jackettPort; + }; + }; + + my.system.persist.directories = [ + config.services.jackett.dataDir + ]; + }) + + (lib.mkIf cfg.nzbhydra.enable { + services.nzbhydra2 = { + enable = true; + }; + + my.services.nginx.virtualHosts = { + nzbhydra = { + port = nzbhydraPort; + }; + }; + + my.system.persist.directories = [ + config.services.nzbhydra2.dataDir + ]; + }) + + (lib.mkIf cfg.prowlarr.enable { + services.prowlarr = { + enable = true; + }; + + my.services.nginx.virtualHosts = { + prowlarr = { + port = prowlarrPort; + }; + }; + + my.system.persist.directories = [ + "/var/lib/prowlarr" + ]; + + services.fail2ban.jails = { + prowlarr = '' + enabled = true + filter = prowlarr + action = iptables-allports + ''; + }; + + environment.etc = { + "fail2ban/filter.d/prowlarr.conf".text = '' + [Definition] + failregex = ^.*\|Warn\|Auth\|Auth-Failure ip username .*$ + journalmatch = _SYSTEMD_UNIT=prowlarr.service + ''; + }; + }) + ]; +} diff --git a/modules/nixos/services/jellyfin/default.nix b/modules/nixos/services/jellyfin/default.nix index 6edeb67..3a421ec 100644 --- a/modules/nixos/services/jellyfin/default.nix +++ b/modules/nixos/services/jellyfin/default.nix @@ -38,6 +38,10 @@ in }; }; + my.system.persist.directories = [ + "/var/lib/jellyfin" + ]; + services.fail2ban.jails = { jellyfin = '' enabled = true diff --git a/modules/nixos/services/komga/default.nix b/modules/nixos/services/komga/default.nix index 9af3cd1..15e4fbb 100644 --- a/modules/nixos/services/komga/default.nix +++ b/modules/nixos/services/komga/default.nix @@ -18,13 +18,13 @@ in config = lib.mkIf cfg.enable { services.komga = { enable = true; + inherit (cfg) port; group = "media"; + }; - settings = { - server.port = cfg.port; - logging.level.org.gotson.komga = "DEBUG"; # Needed for fail2ban - }; + systemd.services.komga.environment = { + LOGGING_LEVEL_ORG_GOTSON_KOMGA = "DEBUG"; # Needed for fail2ban }; # Set-up media group @@ -36,6 +36,10 @@ in }; }; + my.system.persist.directories = [ + config.services.komga.stateDir + ]; + services.fail2ban.jails = { komga = '' enabled = true diff --git a/modules/nixos/services/lohr/default.nix b/modules/nixos/services/lohr/default.nix index 21ed93b..c69075f 100644 --- a/modules/nixos/services/lohr/default.nix +++ b/modules/nixos/services/lohr/default.nix @@ -107,5 +107,9 @@ in }; }; }; + + my.system.persist.directories = [ + "/var/lib/lohr" + ]; }; } diff --git a/modules/nixos/services/matrix/bridges.nix b/modules/nixos/services/matrix/bridges.nix deleted file mode 100644 index 70f4118..0000000 --- a/modules/nixos/services/matrix/bridges.nix +++ /dev/null @@ -1,143 +0,0 @@ -# Matrix bridges for some services I use -{ config, lib, ... }: -let - cfg = config.my.services.matrix.bridges; - synapseCfg = config.services.matrix-synapse; - - domain = config.networking.domain; - serverName = synapseCfg.settings.server_name; - - mkBridgeOption = n: lib.mkEnableOption "${n} bridge" // { default = cfg.enable; }; - mkPortOption = n: default: lib.mkOption { - type = lib.types.port; - inherit default; - example = 8080; - description = "${n} bridge port"; - }; - mkEnvironmentFileOption = n: lib.mkOption { - type = lib.types.str; - example = "/run/secret/matrix/${lib.toLower n}-bridge-secrets.env"; - description = '' - Path to a file which should contain the secret values for ${n} bridge. - - Using through the following format: - - ``` - MATRIX_APPSERVICE_AS_TOKEN= - MATRIX_APPSERVICE_HS_TOKEN= - ``` - - Each bridge should use a different set of secrets, as they each register - their own independent double-puppetting appservice. - ''; - }; -in -{ - options.my.services.matrix.bridges = with lib; { - enable = mkEnableOption "bridges configuration"; - - admin = mkOption { - type = types.str; - default = "ambroisie"; - example = "admin"; - description = "Local username for the admin"; - }; - - facebook = { - enable = mkBridgeOption "Facebook"; - - port = mkPortOption "Facebook" 29321; - - environmentFile = mkEnvironmentFileOption "Facebook"; - }; - }; - - config = lib.mkMerge [ - (lib.mkIf cfg.facebook.enable { - services.mautrix-meta.instances.facebook = { - enable = true; - # Automatically register the bridge with synapse - registerToSynapse = true; - - # Provide `AS_TOKEN`, `HS_TOKEN` - inherit (cfg.facebook) environmentFile; - - settings = { - homeserver = { - domain = serverName; - address = "http://localhost:${toString config.my.services.matrix.port}"; - }; - - appservice = { - hostname = "localhost"; - inherit (cfg.facebook) port; - address = "http://localhost:${toString cfg.facebook.port}"; - public_address = "https://facebook-bridge.${domain}"; - - as_token = "$MATRIX_APPSERVICE_AS_TOKEN"; - hs_token = "$MATRIX_APPSERVICE_HS_TOKEN"; - - bot = { - username = "fbbot"; - }; - }; - - backfill = { - enabled = true; - }; - - bridge = { - delivery_receipts = true; - permissions = { - "*" = "relay"; - ${serverName} = "user"; - "@${cfg.admin}:${serverName}" = "admin"; - }; - }; - - database = { - type = "postgres"; - uri = "postgres:///mautrix-meta-facebook?host=/var/run/postgresql/"; - }; - - double_puppet = { - secrets = { - ${serverName} = "as_token:$MATRIX_APPSERVICE_AS_TOKEN"; - }; - }; - - network = { - # Don't be picky on Facebook/Messenger - allow_messenger_com_on_fb = true; - displayname_template = ''{{or .DisplayName .Username "Unknown user"}} (FB)''; - }; - - provisioning = { - shared_secret = "disable"; - }; - }; - }; - - services.postgresql = { - enable = true; - ensureDatabases = [ "mautrix-meta-facebook" ]; - ensureUsers = [{ - name = "mautrix-meta-facebook"; - ensureDBOwnership = true; - }]; - }; - - systemd.services.mautrix-meta-facebook = { - wants = [ "postgres.service" ]; - after = [ "postgres.service" ]; - }; - - my.services.nginx.virtualHosts = { - # Proxy to the bridge - "facebook-bridge" = { - inherit (cfg.facebook) port; - }; - }; - }) - ]; -} diff --git a/modules/nixos/services/matrix/default.nix b/modules/nixos/services/matrix/default.nix index 97dec2e..cb41a0f 100644 --- a/modules/nixos/services/matrix/default.nix +++ b/modules/nixos/services/matrix/default.nix @@ -1,49 +1,24 @@ -# Matrix homeserver setup. +# Matrix homeserver setup, using different endpoints for federation and client +# traffic. The main trick for this is defining two nginx servers endpoints for +# matrix.domain.com, each listening on different ports. +# +# Configuration shamelessly stolen from [1] +# +# [1]: https://github.com/alarsyo/nixos-config/blob/main/services/matrix.nix { config, lib, pkgs, ... }: let cfg = config.my.services.matrix; - adminPkg = pkgs.synapse-admin-etkecc; - + federationPort = { public = 8448; private = 11338; }; + clientPort = { public = 443; private = 11339; }; domain = config.networking.domain; matrixDomain = "matrix.${domain}"; - - serverConfig = { - "m.server" = "${matrixDomain}:443"; - }; - clientConfig = { - "m.homeserver" = { - "base_url" = "https://${matrixDomain}"; - "server_name" = domain; - }; - "m.identity_server" = { - "base_url" = "https://vector.im"; - }; - }; - - # ACAO required to allow element-web on any URL to request this json file - mkWellKnown = data: '' - default_type application/json; - add_header Access-Control-Allow-Origin *; - return 200 '${builtins.toJSON data}'; - ''; in { - imports = [ - ./bridges.nix - ]; - options.my.services.matrix = with lib; { enable = mkEnableOption "Matrix Synapse"; - port = mkOption { - type = types.port; - default = 8448; - example = 8008; - description = "Internal port for listeners"; - }; - secretFile = mkOption { type = with types; nullOr str; default = null; @@ -83,22 +58,22 @@ in enable_registration = false; listeners = [ + # Federation { - inherit (cfg) port; bind_addresses = [ "::1" ]; - type = "http"; - tls = false; + port = federationPort.private; + tls = false; # Terminated by nginx. x_forwarded = true; - resources = [ - { - names = [ "client" ]; - compress = true; - } - { - names = [ "federation" ]; - compress = false; - } - ]; + resources = [{ names = [ "federation" ]; compress = false; }]; + } + + # Client + { + bind_addresses = [ "::1" ]; + port = clientPort.private; + tls = false; # Terminated by nginx. + x_forwarded = true; + resources = [{ names = [ "client" ]; compress = false; }]; } ]; @@ -121,12 +96,19 @@ in chat = { root = pkgs.element-web.override { conf = { - default_server_config = clientConfig; - show_labs_settings = true; - default_country_code = "FR"; # cocorico - room_directory = { + default_server_config = { + "m.homeserver" = { + "base_url" = "https://${matrixDomain}"; + "server_name" = domain; + }; + "m.identity_server" = { + "base_url" = "https://vector.im"; + }; + }; + showLabsSettings = true; + defaultCountryCode = "FR"; # cocorico + roomDirectory = { "servers" = [ - domain "matrix.org" "mozilla.org" ]; @@ -134,58 +116,107 @@ in }; }; }; - matrix = { - # Somewhat unused, but necessary for port collision detection - inherit (cfg) port; - - extraConfig = { - locations = { - # Or do a redirect instead of the 404, or whatever is appropriate - # for you. But do not put a Matrix Web client here! See the - # Element web section above. - "/".return = "404"; - - "/_matrix".proxyPass = "http://[::1]:${toString cfg.port}"; - "/_synapse".proxyPass = "http://[::1]:${toString cfg.port}"; - - "= /admin".return = "307 /admin/"; - "/admin/" = { - alias = "${adminPkg}/"; - priority = 500; - tryFiles = "$uri $uri/ /index.html"; - }; - "~ ^/admin/.*\\.(?:css|js|jpg|jpeg|gif|png|svg|ico|woff|woff2|ttf|eot|webp)$" = { - priority = 400; - root = adminPkg; - extraConfig = '' - rewrite ^/admin/(.*)$ /$1 break; - expires 30d; - more_set_headers "Cache-Control: public"; - ''; - }; - }; - }; + # Dummy VHosts for port collision detection + matrix-federation = { + port = federationPort.private; + }; + matrix-client = { + port = clientPort.private; }; }; - # Setup well-known locations + # Those are too complicated to use my wrapper... services.nginx.virtualHosts = { + ${matrixDomain} = { + onlySSL = true; + useACMEHost = domain; + + locations = + let + proxyToClientPort = { + proxyPass = "http://[::1]:${toString clientPort.private}"; + }; + in + { + # Or do a redirect instead of the 404, or whatever is appropriate + # for you. But do not put a Matrix Web client here! See the + # Element web section below. + "/".return = "404"; + + "/_matrix" = proxyToClientPort; + "/_synapse/client" = proxyToClientPort; + }; + + listen = [ + { addr = "0.0.0.0"; port = clientPort.public; ssl = true; } + { addr = "[::]"; port = clientPort.public; ssl = true; } + ]; + + }; + + # same as above, but listening on the federation port + "${matrixDomain}_federation" = { + onlySSL = true; + serverName = matrixDomain; + useACMEHost = domain; + + locations."/".return = "404"; + + locations."/_matrix" = { + proxyPass = "http://[::1]:${toString federationPort.private}"; + }; + + listen = [ + { addr = "0.0.0.0"; port = federationPort.public; ssl = true; } + { addr = "[::]"; port = federationPort.public; ssl = true; } + ]; + }; + "${domain}" = { forceSSL = true; useACMEHost = domain; - locations."= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig; - locations."= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig; + locations."= /.well-known/matrix/server".extraConfig = + let + server = { "m.server" = "${matrixDomain}:${toString federationPort.public}"; }; + in + '' + add_header Content-Type application/json; + return 200 '${builtins.toJSON server}'; + ''; + + locations."= /.well-known/matrix/client".extraConfig = + let + client = { + "m.homeserver" = { "base_url" = "https://${matrixDomain}"; }; + "m.identity_server" = { "base_url" = "https://vector.im"; }; + }; + # ACAO required to allow element-web on any URL to request this json file + in + '' + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + return 200 '${builtins.toJSON client}'; + ''; }; }; # For administration tools. environment.systemPackages = [ pkgs.matrix-synapse ]; + networking.firewall.allowedTCPPorts = [ + clientPort.public + federationPort.public + ]; + my.services.backup = { paths = [ config.services.matrix-synapse.dataDir ]; }; + + my.system.persist.directories = [ + config.services.matrix-synapse.dataDir + ]; }; } diff --git a/modules/nixos/services/mealie/default.nix b/modules/nixos/services/mealie/default.nix index f3774e1..630231f 100644 --- a/modules/nixos/services/mealie/default.nix +++ b/modules/nixos/services/mealie/default.nix @@ -32,15 +32,33 @@ in BASE_URL = "https://mealie.${config.networking.domain}"; TZ = config.time.timeZone; ALLOw_SIGNUP = "false"; - TOKEN_TIME = 24 * 180; # 180 days - }; - # Automatic PostgreSQL provisioning - database = { - createLocally = true; + # Use PostgreSQL + DB_ENGINE = "postgres"; + # Make it work with socket auth + POSTGRES_URL_OVERRIDE = "postgresql://mealie:@/mealie?host=/run/postgresql"; }; }; + systemd.services = { + mealie = { + after = [ "postgresql.service" ]; + requires = [ "postgresql.service" ]; + }; + }; + + # Set-up database + services.postgresql = { + enable = true; + ensureDatabases = [ "mealie" ]; + ensureUsers = [ + { + name = "mealie"; + ensureDBOwnership = true; + } + ]; + }; + my.services.nginx.virtualHosts = { mealie = { inherit (cfg) port; diff --git a/modules/nixos/services/monitoring/default.nix b/modules/nixos/services/monitoring/default.nix index 49919c1..4415cb5 100644 --- a/modules/nixos/services/monitoring/default.nix +++ b/modules/nixos/services/monitoring/default.nix @@ -130,5 +130,10 @@ in inherit (cfg.grafana) port; }; }; + + my.system.persist.directories = [ + config.services.grafana.dataDir + "/var/lib/${config.services.prometheus.stateDir}" + ]; }; } diff --git a/modules/nixos/services/navidrome/default.nix b/modules/nixos/services/navidrome/default.nix index c513b91..0fc3539 100644 --- a/modules/nixos/services/navidrome/default.nix +++ b/modules/nixos/services/navidrome/default.nix @@ -53,6 +53,10 @@ in }; }; + my.system.persist.directories = [ + "/var/lib/navidrome" + ]; + services.fail2ban.jails = { navidrome = '' enabled = true diff --git a/modules/nixos/services/nextcloud/collabora.nix b/modules/nixos/services/nextcloud/collabora.nix index 408b232..dce1a99 100644 --- a/modules/nixos/services/nextcloud/collabora.nix +++ b/modules/nixos/services/nextcloud/collabora.nix @@ -16,12 +16,6 @@ in }; config = lib.mkIf cfg.enable { - services.nextcloud = { - extraApps = { - inherit (config.services.nextcloud.package.packages.apps) richdocuments; - }; - }; - services.collabora-online = { enable = true; inherit (cfg) port; @@ -52,5 +46,7 @@ in ]; }; }; + + # FIXME: persistence? }; } diff --git a/modules/nixos/services/nextcloud/default.nix b/modules/nixos/services/nextcloud/default.nix index 24515ff..5452dac 100644 --- a/modules/nixos/services/nextcloud/default.nix +++ b/modules/nixos/services/nextcloud/default.nix @@ -35,7 +35,7 @@ in config = lib.mkIf cfg.enable { services.nextcloud = { enable = true; - package = pkgs.nextcloud32; + package = pkgs.nextcloud30; hostName = "nextcloud.${config.networking.domain}"; home = "/var/lib/nextcloud"; maxUploadSize = cfg.maxSize; @@ -44,15 +44,11 @@ in adminuser = cfg.admin; adminpassFile = cfg.passwordFile; dbtype = "pgsql"; + dbhost = "/run/postgresql"; }; https = true; - # Automatic PostgreSQL provisioning - database = { - createLocally = true; - }; - settings = { overwriteprotocol = "https"; # Nginx only allows SSL }; @@ -62,16 +58,22 @@ in # Allow using the push service without hard-coding my IP in the configuration bendDomainToLocalhost = true; }; + }; - extraApps = { - inherit (config.services.nextcloud.package.packages.apps) - calendar - contacts - deck - tasks - ; - # notify_push is automatically installed by the module - }; + services.postgresql = { + enable = true; + ensureDatabases = [ "nextcloud" ]; + ensureUsers = [ + { + name = "nextcloud"; + ensureDBOwnership = true; + } + ]; + }; + + systemd.services."nextcloud-setup" = { + requires = [ "postgresql.service" ]; + after = [ "postgresql.service" ]; }; # The service above configures the domain, no need for my wrapper @@ -90,6 +92,10 @@ in ]; }; + my.system.persist.directories = [ + config.services.nextcloud.home + ]; + services.fail2ban.jails = { nextcloud = '' enabled = true diff --git a/modules/nixos/services/nginx/default.nix b/modules/nixos/services/nginx/default.nix index ff530b0..ab69efd 100644 --- a/modules/nixos/services/nginx/default.nix +++ b/modules/nixos/services/nginx/default.nix @@ -87,6 +87,10 @@ let }); in { + imports = [ + ./sso + ]; + options.my.services.nginx = with lib; { enable = mkEnableOption "Nginx"; @@ -253,6 +257,7 @@ in recommendedOptimisation = true; recommendedProxySettings = true; recommendedTlsSettings = true; + recommendedZstdSettings = true; virtualHosts = let @@ -444,7 +449,7 @@ in }; }; - systemd.services."acme-order-renew-${domain}" = { + systemd.services."acme-${domain}" = { serviceConfig = { Environment = [ # Since I do a "weird" setup with a wildcard CNAME @@ -481,5 +486,9 @@ in } ]; }; + + my.system.persist.directories = [ + "/var/lib/acme" + ]; }; } diff --git a/modules/nixos/services/nginx/sso/default.nix b/modules/nixos/services/nginx/sso/default.nix new file mode 100644 index 0000000..d60e31b --- /dev/null +++ b/modules/nixos/services/nginx/sso/default.nix @@ -0,0 +1,84 @@ +# I must override the module to allow having runtime secrets +{ config, lib, pkgs, utils, ... }: +let + cfg = config.services.nginx.sso; + pkg = lib.getBin cfg.package; + confPath = "/var/lib/nginx-sso/config.json"; +in +{ + disabledModules = [ "services/security/nginx-sso.nix" ]; + + + options.services.nginx.sso = with lib; { + enable = mkEnableOption "nginx-sso service"; + + package = mkOption { + type = types.package; + default = pkgs.nginx-sso; + defaultText = "pkgs.nginx-sso"; + description = '' + The nginx-sso package that should be used. + ''; + }; + + configuration = mkOption { + type = types.attrsOf types.unspecified; + default = { }; + example = literalExample '' + { + listen = { addr = "127.0.0.1"; port = 8080; }; + + providers.token.tokens = { + myuser = "MyToken"; + }; + + acl = { + rule_sets = [ + { + rules = [ { field = "x-application"; equals = "MyApp"; } ]; + allow = [ "myuser" ]; + } + ]; + }; + } + ''; + description = '' + nginx-sso configuration + (documentation) + as a Nix attribute set. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.nginx-sso = { + description = "Nginx SSO Backend"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + StateDirectory = "nginx-sso"; + WorkingDirectory = "/var/lib/nginx-sso"; + # The files to be merged might not have the correct permissions + ExecStartPre = pkgs.writeShellScript "merge-nginx-sso-config" '' + rm -f '${confPath}' + ${utils.genJqSecretsReplacementSnippet cfg.configuration confPath} + ''; + ExecStart = lib.mkForce '' + ${lib.getExe pkg} \ + --config ${confPath} \ + --frontend-dir ${pkg}/share/frontend + ''; + Restart = "always"; + User = "nginx-sso"; + Group = "nginx-sso"; + }; + }; + + users.users.nginx-sso = { + isSystemUser = true; + group = "nginx-sso"; + }; + + users.groups.nginx-sso = { }; + }; +} diff --git a/modules/nixos/services/paperless/default.nix b/modules/nixos/services/paperless/default.nix index 1195977..0754721 100644 --- a/modules/nixos/services/paperless/default.nix +++ b/modules/nixos/services/paperless/default.nix @@ -52,39 +52,75 @@ in mediaDir = lib.mkIf (cfg.documentPath != null) cfg.documentPath; - settings = { - # Use SSO - PAPERLESS_ENABLE_HTTP_REMOTE_USER = true; - PAPERLESS_ENABLE_HTTP_REMOTE_USER_API = true; - PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME = "HTTP_X_USER"; + settings = + let + paperlessDomain = "paperless.${config.networking.domain}"; + in + { + # Use SSO + PAPERLESS_ENABLE_HTTP_REMOTE_USER = true; + PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME = "HTTP_X_USER"; - # Security settings - PAPERLESS_URL = "https://paperless.${config.networking.domain}"; - PAPERLESS_USE_X_FORWARD_HOST = true; - PAPERLESS_PROXY_SSL_HEADER = [ "HTTP_X_FORWARDED_PROTO" "https" ]; + # Use PostgreSQL + PAPERLESS_DBHOST = "/run/postgresql"; + PAPERLESS_DBUSER = "paperless"; + PAPERLESS_DBNAME = "paperless"; - # OCR settings - PAPERLESS_OCR_LANGUAGE = "fra+eng"; + # Security settings + PAPERLESS_ALLOWED_HOSTS = paperlessDomain; + PAPERLESS_CORS_ALLOWED_HOSTS = "https://${paperlessDomain}"; - # Workers - PAPERLESS_TASK_WORKERS = 3; - PAPERLESS_THREADS_PER_WORKER = 4; + # OCR settings + PAPERLESS_OCR_LANGUAGE = "fra+eng"; - # Misc - PAPERLESS_TIME_ZONE = config.time.timeZone; - PAPERLESS_ADMIN_USER = cfg.username; - }; + # Workers + PAPERLESS_TASK_WORKERS = 3; + PAPERLESS_THREADS_PER_WORKER = 4; + + # Misc + PAPERLESS_TIME_ZONE = config.time.timeZone; + PAPERLESS_ADMIN_USER = cfg.username; + }; # Admin password passwordFile = cfg.passwordFile; # Secret key environmentFile = cfg.secretKeyFile; + }; - # Automatic PostgreSQL provisioning - database = { - createLocally = true; + systemd.services = { + paperless-scheduler = { + requires = [ "postgresql.service" ]; + after = [ "postgresql.service" ]; }; + + paperless-consumer = { + requires = [ "postgresql.service" ]; + after = [ "postgresql.service" ]; + }; + + paperless-web = { + requires = [ "postgresql.service" ]; + after = [ "postgresql.service" ]; + }; + + paperless-task-queue = { + requires = [ "postgresql.service" ]; + after = [ "postgresql.service" ]; + }; + }; + + # Set-up database + services.postgresql = { + enable = true; + ensureDatabases = [ "paperless" ]; + ensureUsers = [ + { + name = "paperless"; + ensureDBOwnership = true; + } + ]; }; # Set-up media group @@ -110,5 +146,10 @@ in config.services.paperless.mediaDir ]; }; + + my.system.persist.directories = [ + config.services.paperless.dataDir + config.services.paperless.mediaDir + ]; }; } diff --git a/modules/nixos/services/pdf-edit/default.nix b/modules/nixos/services/pdf-edit/default.nix index d59507b..180e520 100644 --- a/modules/nixos/services/pdf-edit/default.nix +++ b/modules/nixos/services/pdf-edit/default.nix @@ -54,6 +54,10 @@ in }; }; + my.system.persist.directories = [ + "/var/lib/stirling-pdf" + ]; + services.fail2ban.jails = { stirling-pdf = '' enabled = true diff --git a/modules/nixos/services/podgrab/default.nix b/modules/nixos/services/podgrab/default.nix index 3ced8d3..ec6ecb2 100644 --- a/modules/nixos/services/podgrab/default.nix +++ b/modules/nixos/services/podgrab/default.nix @@ -51,5 +51,10 @@ in inherit (cfg) port; }; }; + + my.system.persist.directories = [ + config.systemd.services.podgrab.environment.CONFIG + config.systemd.services.podgrab.environment.DATA + ]; }; } diff --git a/modules/nixos/services/postgresql-backup/default.nix b/modules/nixos/services/postgresql-backup/default.nix index dff5494..3d6c03b 100644 --- a/modules/nixos/services/postgresql-backup/default.nix +++ b/modules/nixos/services/postgresql-backup/default.nix @@ -24,5 +24,9 @@ in (config.services.postgresqlBackup.location + "/*.prev.sql.gz") ]; }; + + my.system.persist.directories = [ + config.services.postgresqlBackup.location + ]; }; } diff --git a/modules/nixos/services/postgresql/default.nix b/modules/nixos/services/postgresql/default.nix index 1dca164..cea4c88 100644 --- a/modules/nixos/services/postgresql/default.nix +++ b/modules/nixos/services/postgresql/default.nix @@ -14,24 +14,31 @@ in # Let other services enable postgres when they need it (lib.mkIf cfg.enable { services.postgresql = { - package = pkgs.postgresql_17; + package = pkgs.postgresql_13; }; }) + # Only persist directory if the actual service is enabled + (lib.mkIf config.services.postgresql.enable { + my.system.persist.directories = [ + config.services.postgresql.dataDir + ]; + }) + # Taken from the manual (lib.mkIf cfg.upgradeScript { environment.systemPackages = let pgCfg = config.services.postgresql; - newPackage' = pkgs.postgresql_17; + newPackage' = pkgs.postgresql_13; oldPackage = if pgCfg.enableJIT then pgCfg.package.withJIT else pgCfg.package; oldData = pgCfg.dataDir; - oldBin = "${if pgCfg.extensions == [] then oldPackage else oldPackage.withPackages pgCfg.extensions}/bin"; + oldBin = "${if pgCfg.extraPlugins == [] then oldPackage else oldPackage.withPackages pgCfg.extraPlugins}/bin"; newPackage = if pgCfg.enableJIT then newPackage'.withJIT else newPackage'; newData = "/var/lib/postgresql/${newPackage.psqlSchema}"; - newBin = "${if pgCfg.extensions == [] then newPackage else newPackage.withPackages pgCfg.extensions}/bin"; + newBin = "${if pgCfg.extraPlugins == [] then newPackage else newPackage.withPackages pgCfg.extraPlugins}/bin"; in [ (pkgs.writeScriptBin "upgrade-pg-cluster" '' diff --git a/modules/nixos/services/pyload/default.nix b/modules/nixos/services/pyload/default.nix index 7257d0f..ec5042b 100644 --- a/modules/nixos/services/pyload/default.nix +++ b/modules/nixos/services/pyload/default.nix @@ -53,6 +53,11 @@ in }; }; + my.system.persist.directories = [ + cfg.downloadDirectory + "/var/lib/pyload" + ]; + services.fail2ban.jails = { pyload = '' enabled = true diff --git a/modules/nixos/services/quassel/default.nix b/modules/nixos/services/quassel/default.nix index 695f9e0..0065195 100644 --- a/modules/nixos/services/quassel/default.nix +++ b/modules/nixos/services/quassel/default.nix @@ -46,5 +46,9 @@ in # Because Quassel does not use the socket, I simply trust its connection authentication = "host quassel quassel localhost trust"; }; + + my.system.persist.directories = [ + config.services.quassel.dataDir + ]; }; } diff --git a/modules/nixos/services/rss-bridge/default.nix b/modules/nixos/services/rss-bridge/default.nix index 52b1030..977b431 100644 --- a/modules/nixos/services/rss-bridge/default.nix +++ b/modules/nixos/services/rss-bridge/default.nix @@ -22,5 +22,9 @@ in forceSSL = true; useACMEHost = config.networking.domain; }; + + my.system.persist.directories = [ + config.services.rss-bridge.dataDir + ]; }; } diff --git a/modules/nixos/services/sabnzbd/default.nix b/modules/nixos/services/sabnzbd/default.nix index 9e0d9c3..86202ab 100644 --- a/modules/nixos/services/sabnzbd/default.nix +++ b/modules/nixos/services/sabnzbd/default.nix @@ -24,6 +24,10 @@ in }; }; + my.system.persist.files = [ + config.services.sabnzbd.configFile + ]; + services.fail2ban.jails = { sabnzbd = '' enabled = true diff --git a/modules/nixos/services/servarr/autobrr.nix b/modules/nixos/services/servarr/autobrr.nix deleted file mode 100644 index c3370cb..0000000 --- a/modules/nixos/services/servarr/autobrr.nix +++ /dev/null @@ -1,63 +0,0 @@ -# IRC-based indexer -{ config, lib, ... }: -let - cfg = config.my.services.servarr.autobrr; -in -{ - options.my.services.servarr.autobrr = with lib; { - enable = mkEnableOption "autobrr IRC announce tracker" // { - default = config.my.services.servarr.enableAll; - }; - - port = mkOption { - type = types.port; - default = 7474; - example = 8080; - description = "Internal port for webui"; - }; - - sessionSecretFile = mkOption { - type = types.str; - example = "/run/secrets/autobrr-secret.txt"; - description = '' - File containing the session secret. - ''; - }; - }; - - config = lib.mkIf cfg.enable { - services.autobrr = { - enable = true; - - settings = { - inherit (cfg) port; - checkForUpdates = false; - }; - - secretFile = cfg.sessionSecretFile; - }; - - my.services.nginx.virtualHosts = { - autobrr = { - inherit (cfg) port; - websocketsLocations = [ "/api" ]; - }; - }; - - services.fail2ban.jails = { - autobrr = '' - enabled = true - filter = autobrr - action = iptables-allports - ''; - }; - - environment.etc = { - "fail2ban/filter.d/autobrr.conf".text = '' - [Definition] - failregex = "message":"Auth: Failed login attempt username: \[.*\] ip: " - journalmatch = _SYSTEMD_UNIT=autobrr.service - ''; - }; - }; -} diff --git a/modules/nixos/services/servarr/bazarr.nix b/modules/nixos/services/servarr/bazarr.nix deleted file mode 100644 index 637da0c..0000000 --- a/modules/nixos/services/servarr/bazarr.nix +++ /dev/null @@ -1,37 +0,0 @@ -{ config, lib, ... }: -let - cfg = config.my.services.servarr.bazarr; -in -{ - options.my.services.servarr.bazarr = with lib; { - enable = lib.mkEnableOption "Bazarr" // { - default = config.my.services.servarr.enableAll; - }; - - port = mkOption { - type = types.port; - default = 6767; - example = 8080; - description = "Internal port for webui"; - }; - }; - - config = lib.mkIf cfg.enable { - services.bazarr = { - enable = true; - group = "media"; - listenPort = cfg.port; - }; - - # Set-up media group - users.groups.media = { }; - - my.services.nginx.virtualHosts = { - bazarr = { - inherit (cfg) port; - }; - }; - - # Bazarr does not log authentication failures... - }; -} diff --git a/modules/nixos/services/servarr/cross-seed.nix b/modules/nixos/services/servarr/cross-seed.nix deleted file mode 100644 index 74f216a..0000000 --- a/modules/nixos/services/servarr/cross-seed.nix +++ /dev/null @@ -1,96 +0,0 @@ -# Automatic cross-seeding for video media -{ config, lib, ... }: -let - cfg = config.my.services.servarr.cross-seed; -in -{ - options.my.services.servarr.cross-seed = with lib; { - enable = mkEnableOption "cross-seed daemon" // { - default = config.my.services.servarr.enableAll; - }; - - port = mkOption { - type = types.port; - default = 2468; - example = 8080; - description = "Internal port for daemon"; - }; - - linkDirectory = mkOption { - type = types.str; - default = "/data/downloads/complete/links"; - example = "/var/lib/cross-seed/links"; - description = "Link directory"; - }; - - secretSettingsFile = mkOption { - type = types.str; - example = "/run/secrets/cross-seed-secrets.json"; - description = '' - File containing secret settings. - ''; - }; - }; - - config = lib.mkIf cfg.enable { - services.cross-seed = { - enable = true; - group = "media"; - - # Rely on recommended defaults for tracker snatches etc... - useGenConfigDefaults = true; - - settings = { - inherit (cfg) port; - host = "127.0.0.1"; - - # Inject torrents to client directly - action = "inject"; - # Query the client for torrents to match - useClientTorrents = true; - # Use hardlinks - linkType = "hardlink"; - # Use configured link directory - linkDirs = [ cfg.linkDirectory ]; - # Match as many torrents as possible - matchMode = "partial"; - # Cross-seed full season if at least 50% of episodes are already downloaded - seasonFromEpisodes = 0.5; - }; - - settingsFile = cfg.secretSettingsFile; - }; - - systemd.services.cross-seed = { - serviceConfig = { - # Loose umask to make cross-seed links readable by `media` - UMask = "0002"; - }; - }; - - # Set-up media group - users.groups.media = { }; - - my.services.nginx.virtualHosts = { - cross-seed = { - inherit (cfg) port; - }; - }; - - services.fail2ban.jails = { - cross-seed = '' - enabled = true - filter = cross-seed - action = iptables-allports - ''; - }; - - environment.etc = { - "fail2ban/filter.d/cross-seed.conf".text = '' - [Definition] - failregex = ^.*Unauthorized API access attempt to .* from $ - journalmatch = _SYSTEMD_UNIT=cross-seed.service - ''; - }; - }; -} diff --git a/modules/nixos/services/servarr/default.nix b/modules/nixos/services/servarr/default.nix index dca57cf..c0f57df 100644 --- a/modules/nixos/services/servarr/default.nix +++ b/modules/nixos/services/servarr/default.nix @@ -2,22 +2,109 @@ # Relevant link [1]. # # [1]: https://youtu.be/I26Ql-uX6AM -{ lib, ... }: -{ - imports = [ - ./autobrr.nix - ./bazarr.nix - ./cross-seed.nix - ./jackett.nix - ./nzbhydra.nix - ./prowlarr.nix - (import ./starr.nix "lidarr") - (import ./starr.nix "radarr") - (import ./starr.nix "readarr") - (import ./starr.nix "sonarr") - ]; +{ config, lib, ... }: +let + cfg = config.my.services.servarr; - options.my.services.servarr = { - enableAll = lib.mkEnableOption "media automation suite"; + ports = { + bazarr = 6767; + lidarr = 8686; + radarr = 7878; + readarr = 8787; + sonarr = 8989; }; + + mkService = service: { + services.${service} = { + enable = true; + group = "media"; + }; + + my.system.persist.directories = + let + # Bazarr breaks the mold unfortunately + dataDir = + if service != "bazarr" + then config.services.${service}.dataDir + else "/var/lib/bazarr"; + in + [ dataDir ]; + }; + + mkRedirection = service: { + my.services.nginx.virtualHosts = { + ${service} = { + port = ports.${service}; + }; + }; + }; + + mkFail2Ban = service: lib.mkIf cfg.${service}.enable { + services.fail2ban.jails = { + ${service} = '' + enabled = true + filter = ${service} + action = iptables-allports + ''; + }; + + environment.etc = { + "fail2ban/filter.d/${service}.conf".text = '' + [Definition] + failregex = ^.*\|Warn\|Auth\|Auth-Failure ip username .*$ + journalmatch = _SYSTEMD_UNIT=${service}.service + ''; + }; + }; + + mkFullConfig = service: lib.mkIf cfg.${service}.enable (lib.mkMerge [ + (mkService service) + (mkRedirection service) + ]); +in +{ + options.my.services.servarr = { + enable = lib.mkEnableOption "Media automation"; + + bazarr = { + enable = lib.my.mkDisableOption "Bazarr"; + }; + + lidarr = { + enable = lib.my.mkDisableOption "Lidarr"; + }; + + radarr = { + enable = lib.my.mkDisableOption "Radarr"; + }; + + readarr = { + enable = lib.my.mkDisableOption "Readarr"; + }; + + sonarr = { + enable = lib.my.mkDisableOption "Sonarr"; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + # Set-up media group + users.groups.media = { }; + } + # Bazarr does not log authentication failures... + (mkFullConfig "bazarr") + # Lidarr for music + (mkFullConfig "lidarr") + (mkFail2Ban "lidarr") + # Radarr for movies + (mkFullConfig "radarr") + (mkFail2Ban "radarr") + # Readarr for books + (mkFullConfig "readarr") + (mkFail2Ban "readarr") + # Sonarr for shows + (mkFullConfig "sonarr") + (mkFail2Ban "sonarr") + ]); } diff --git a/modules/nixos/services/servarr/jackett.nix b/modules/nixos/services/servarr/jackett.nix deleted file mode 100644 index 481cd3d..0000000 --- a/modules/nixos/services/servarr/jackett.nix +++ /dev/null @@ -1,41 +0,0 @@ -{ config, lib, ... }: -let - cfg = config.my.services.servarr.jackett; -in -{ - options.my.services.servarr.jackett = with lib; { - enable = lib.mkEnableOption "Jackett" // { - default = config.my.services.servarr.enableAll; - }; - - port = mkOption { - type = types.port; - default = 9117; - example = 8080; - description = "Internal port for webui"; - }; - }; - - config = lib.mkIf cfg.enable { - services.jackett = { - enable = true; - inherit (cfg) port; - }; - - # Jackett wants to eat *all* my RAM if left to its own devices - systemd.services.jackett = { - serviceConfig = { - MemoryHigh = "15%"; - MemoryMax = "25%"; - }; - }; - - my.services.nginx.virtualHosts = { - jackett = { - inherit (cfg) port; - }; - }; - - # Jackett does not log authentication failures... - }; -} diff --git a/modules/nixos/services/servarr/nzbhydra.nix b/modules/nixos/services/servarr/nzbhydra.nix deleted file mode 100644 index 7b63986..0000000 --- a/modules/nixos/services/servarr/nzbhydra.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ config, lib, ... }: -let - cfg = config.my.services.servarr.nzbhydra; -in -{ - options.my.services.servarr.nzbhydra = with lib; { - enable = lib.mkEnableOption "NZBHydra2" // { - default = config.my.services.servarr.enableAll; - }; - }; - - config = lib.mkIf cfg.enable { - services.nzbhydra2 = { - enable = true; - }; - - my.services.nginx.virtualHosts = { - nzbhydra = { - port = 5076; - websocketsLocations = [ "/" ]; - }; - }; - - # NZBHydra2 does not log authentication failures... - }; -} diff --git a/modules/nixos/services/servarr/prowlarr.nix b/modules/nixos/services/servarr/prowlarr.nix deleted file mode 100644 index ce044c6..0000000 --- a/modules/nixos/services/servarr/prowlarr.nix +++ /dev/null @@ -1,53 +0,0 @@ -# Torrent and NZB indexer -{ config, lib, ... }: -let - cfg = config.my.services.servarr.prowlarr; -in -{ - options.my.services.servarr.prowlarr = with lib; { - enable = lib.mkEnableOption "Prowlarr" // { - default = config.my.services.servarr.enableAll; - }; - - port = mkOption { - type = types.port; - default = 9696; - example = 8080; - description = "Internal port for webui"; - }; - }; - - config = lib.mkIf cfg.enable { - services.prowlarr = { - enable = true; - - settings = { - server = { - port = cfg.port; - }; - }; - }; - - my.services.nginx.virtualHosts = { - prowlarr = { - inherit (cfg) port; - }; - }; - - services.fail2ban.jails = { - prowlarr = '' - enabled = true - filter = prowlarr - action = iptables-allports - ''; - }; - - environment.etc = { - "fail2ban/filter.d/prowlarr.conf".text = '' - [Definition] - failregex = ^.*\|Warn\|Auth\|Auth-Failure ip username .*$ - journalmatch = _SYSTEMD_UNIT=prowlarr.service - ''; - }; - }; -} diff --git a/modules/nixos/services/servarr/starr.nix b/modules/nixos/services/servarr/starr.nix deleted file mode 100644 index 2bf7c11..0000000 --- a/modules/nixos/services/servarr/starr.nix +++ /dev/null @@ -1,64 +0,0 @@ -# Templated *arr configuration -starr: -{ config, lib, ... }: -let - cfg = config.my.services.servarr.${starr}; - ports = { - lidarr = 8686; - radarr = 7878; - readarr = 8787; - sonarr = 8989; - }; -in -{ - options.my.services.servarr.${starr} = with lib; { - enable = lib.mkEnableOption (lib.toSentenceCase starr) // { - default = config.my.services.servarr.enableAll; - }; - - port = mkOption { - type = types.port; - default = ports.${starr}; - example = 8080; - description = "Internal port for webui"; - }; - }; - - config = lib.mkIf cfg.enable { - services.${starr} = { - enable = true; - group = "media"; - - settings = { - server = { - port = cfg.port; - }; - }; - }; - - # Set-up media group - users.groups.media = { }; - - my.services.nginx.virtualHosts = { - ${starr} = { - port = cfg.port; - }; - }; - - services.fail2ban.jails = { - ${starr} = '' - enabled = true - filter = ${starr} - action = iptables-allports - ''; - }; - - environment.etc = { - "fail2ban/filter.d/${starr}.conf".text = '' - [Definition] - failregex = ^.*\|Warn\|Auth\|Auth-Failure ip username .*$ - journalmatch = _SYSTEMD_UNIT=${starr}.service - ''; - }; - }; -} diff --git a/modules/nixos/services/ssh-server/default.nix b/modules/nixos/services/ssh-server/default.nix index 9ae0fa8..c4d1fd6 100644 --- a/modules/nixos/services/ssh-server/default.nix +++ b/modules/nixos/services/ssh-server/default.nix @@ -20,6 +20,13 @@ in }; }; + # Persist SSH keys + my.system.persist.files = + let + pubAndPrivKey = key: [ key.path "${key.path}.pub" ]; + in + lib.concatMap pubAndPrivKey config.services.openssh.hostKeys; + # Opens the relevant UDP ports. programs.mosh.enable = true; }; diff --git a/modules/nixos/services/tandoor-recipes/default.nix b/modules/nixos/services/tandoor-recipes/default.nix index 4b4ed1a..0bfa9fe 100644 --- a/modules/nixos/services/tandoor-recipes/default.nix +++ b/modules/nixos/services/tandoor-recipes/default.nix @@ -26,16 +26,18 @@ in services.tandoor-recipes = { enable = true; - database = { - createLocally = true; - }; - port = cfg.port; extraConfig = let tandoorRecipesDomain = "recipes.${config.networking.domain}"; in { + # Use PostgreSQL + DB_ENGINE = "django.db.backends.postgresql"; + POSTGRES_HOST = "/run/postgresql"; + POSTGRES_USER = "tandoor_recipes"; + POSTGRES_DB = "tandoor_recipes"; + # Security settings ALLOWED_HOSTS = tandoorRecipesDomain; CSRF_TRUSTED_ORIGINS = "https://${tandoorRecipesDomain}"; @@ -47,12 +49,27 @@ in systemd.services = { tandoor-recipes = { + after = [ "postgresql.service" ]; + requires = [ "postgresql.service" ]; + serviceConfig = { EnvironmentFile = cfg.secretKeyFile; }; }; }; + # Set-up database + services.postgresql = { + enable = true; + ensureDatabases = [ "tandoor_recipes" ]; + ensureUsers = [ + { + name = "tandoor_recipes"; + ensureDBOwnership = true; + } + ]; + }; + my.services.nginx.virtualHosts = { recipes = { inherit (cfg) port; @@ -66,6 +83,16 @@ in }; }; + my.services.backup = { + paths = [ + "/var/lib/tandoor-recipes" + ]; + }; + + my.system.persist.directories = [ + "/var/lib/tandoor-recipes" + ]; + # NOTE: unfortunately tandoor-recipes does not log connection failures for fail2ban }; } diff --git a/modules/nixos/services/thelounge/default.nix b/modules/nixos/services/thelounge/default.nix deleted file mode 100644 index e224839..0000000 --- a/modules/nixos/services/thelounge/default.nix +++ /dev/null @@ -1,59 +0,0 @@ -# Web IRC client -{ config, lib, ... }: -let - cfg = config.my.services.thelounge; -in -{ - options.my.services.thelounge = with lib; { - enable = mkEnableOption "The Lounge, a self-hosted web IRC client"; - - port = mkOption { - type = types.port; - default = 9050; - example = 4242; - description = "The port on which The Lounge will listen for incoming HTTP traffic."; - }; - }; - - config = lib.mkIf cfg.enable { - services.thelounge = { - enable = true; - inherit (cfg) port; - - extraConfig = { - reverseProxy = true; - }; - }; - - my.services.nginx.virtualHosts = { - irc = { - inherit (cfg) port; - # Proxy websockets for RPC - websocketsLocations = [ "/" ]; - - extraConfig = { - locations."/".extraConfig = '' - proxy_read_timeout 1d; - ''; - }; - }; - }; - - services.fail2ban.jails = { - thelounge = '' - enabled = true - filter = thelounge - port = http,https - ''; - }; - - environment.etc = { - "fail2ban/filter.d/thelounge.conf".text = '' - [Definition] - failregex = Authentication failed for user .* from $ - Authentication for non existing user attempted from $ - journalmatch = _SYSTEMD_UNIT=thelounge.service - ''; - }; - }; -} diff --git a/modules/nixos/services/transmission/default.nix b/modules/nixos/services/transmission/default.nix index 6a7fbc7..a5393eb 100644 --- a/modules/nixos/services/transmission/default.nix +++ b/modules/nixos/services/transmission/default.nix @@ -47,7 +47,6 @@ in enable = true; package = pkgs.transmission_4; group = "media"; - webHome = pkgs.trgui-ng-web; downloadDirPermissions = "775"; @@ -66,19 +65,13 @@ in # Proxied behind Nginx. rpc-whitelist-enabled = true; rpc-whitelist = "127.0.0.1"; - - umask = "002"; # To go with `downloadDirPermissions` }; }; + # Transmission wants to eat *all* my RAM if left to its own devices systemd.services.transmission = { serviceConfig = { - # Transmission wants to eat *all* my RAM if left to its own devices MemoryMax = "33%"; - # Avoid errors due to high number of open files. - LimitNOFILE = 1048576; - # Longer stop timeout to finish all torrents - TimeoutStopSec = "5m"; }; }; @@ -98,6 +91,11 @@ in allowedUDPPorts = [ cfg.peerPort ]; }; + my.system.persist.directories = [ + cfg.downloadBase + config.services.transmission.home + ]; + # NOTE: unfortunately transmission does not log connection failures for fail2ban }; } diff --git a/modules/nixos/services/vikunja/default.nix b/modules/nixos/services/vikunja/default.nix index 2753da3..d902449 100644 --- a/modules/nixos/services/vikunja/default.nix +++ b/modules/nixos/services/vikunja/default.nix @@ -100,6 +100,10 @@ in ]; }; + my.system.persist.directories = [ + config.services.vikunja.settings.files.basepath + ]; + # NOTE: unfortunately vikunja does not log connection failures for fail2ban }; } diff --git a/modules/nixos/services/woodpecker/agent-docker/default.nix b/modules/nixos/services/woodpecker/agent-docker/default.nix index 79d3299..2e74b67 100644 --- a/modules/nixos/services/woodpecker/agent-docker/default.nix +++ b/modules/nixos/services/woodpecker/agent-docker/default.nix @@ -38,5 +38,7 @@ in ]; }; }; + + # FIXME: persistence? }; } diff --git a/modules/nixos/services/woodpecker/agent-exec/default.nix b/modules/nixos/services/woodpecker/agent-exec/default.nix index 24161b0..4210242 100644 --- a/modules/nixos/services/woodpecker/agent-exec/default.nix +++ b/modules/nixos/services/woodpecker/agent-exec/default.nix @@ -62,5 +62,7 @@ in ]; }; }; + + # FIXME: persistence? }; } diff --git a/modules/nixos/services/woodpecker/server/default.nix b/modules/nixos/services/woodpecker/server/default.nix index caf0179..5d25284 100644 --- a/modules/nixos/services/woodpecker/server/default.nix +++ b/modules/nixos/services/woodpecker/server/default.nix @@ -24,8 +24,8 @@ in }; systemd.services.woodpecker-server = { - after = [ "postgresql.target" ]; - requires = [ "postgresql.target" ]; + after = [ "postgresql.service" ]; + requires = [ "postgresql.service" ]; serviceConfig = { # Set username for DB access @@ -61,5 +61,7 @@ in port = cfg.rpcPort; }; }; + + # FIXME: persistence }; } diff --git a/modules/nixos/system/default.nix b/modules/nixos/system/default.nix index e6fb25b..3531847 100644 --- a/modules/nixos/system/default.nix +++ b/modules/nixos/system/default.nix @@ -9,6 +9,7 @@ ./language ./nix ./packages + ./persist ./podman ./polkit ./printing diff --git a/modules/nixos/system/docker/default.nix b/modules/nixos/system/docker/default.nix index f051814..cab9fb5 100644 --- a/modules/nixos/system/docker/default.nix +++ b/modules/nixos/system/docker/default.nix @@ -23,5 +23,9 @@ in ]; }; }; + + my.system.persist.directories = [ + "/var/lib/docker" + ]; }; } diff --git a/modules/nixos/system/persist/default.nix b/modules/nixos/system/persist/default.nix new file mode 100644 index 0000000..ac1820e --- /dev/null +++ b/modules/nixos/system/persist/default.nix @@ -0,0 +1,71 @@ +# Ephemeral root configuration +{ config, inputs, lib, ... }: +let + cfg = config.my.system.persist; +in +{ + imports = [ + inputs.impermanence.nixosModules.impermanence + ]; + + options.my.system.persist = with lib; { + enable = mkEnableOption "stateless system configuration"; + + mountPoint = lib.mkOption { + type = types.str; + default = "/persistent"; + example = "/etc/nix/persist"; + description = '' + Which mount point should be used to persist this system's files and + directories. + ''; + }; + + files = lib.mkOption { + type = with types; listOf str; + default = [ ]; + example = [ + "/etc/nix/id_rsa" + ]; + description = '' + Additional files in the root to link to persistent storage. + ''; + }; + + directories = lib.mkOption { + type = with types; listOf str; + default = [ ]; + example = [ + "/var/lib/libvirt" + ]; + description = '' + Additional directories in the root to link to persistent storage. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + environment.persistence."${cfg.mountPoint}" = { + files = [ + "/etc/machine-id" + "/etc/adjtime" + "/var/lib/systemd/timesync/clock" + ] + ++ lib.unique cfg.files + ; + + directories = [ + "/etc/nixos" # In case it's storage directory of our configuration + "/var/log" + "/var/lib/nixos" # UID/GID maps + "/var/lib/systemd/coredump" + + "/var/lib/systemd" # FIXME: needed? + "/var/spool" # FIXME: needed? + "/var/tmp" # FIXME: needed? + ] + ++ lib.unique cfg.directories + ; + }; + }; +} diff --git a/modules/nixos/system/podman/default.nix b/modules/nixos/system/podman/default.nix index 52630c7..8400dfd 100644 --- a/modules/nixos/system/podman/default.nix +++ b/modules/nixos/system/podman/default.nix @@ -44,5 +44,9 @@ in ]; }; }; + + my.system.persist.directories = [ + "/var/lib/containers" + ]; }; } diff --git a/modules/nixos/system/printing/default.nix b/modules/nixos/system/printing/default.nix index 0dfab0f..3e21b25 100644 --- a/modules/nixos/system/printing/default.nix +++ b/modules/nixos/system/printing/default.nix @@ -65,5 +65,7 @@ in # Allow resolution of '.local' addresses nssmdns4 = true; }; + + # FIXME: persistence? }; } diff --git a/pkgs/bw-pass/bw-pass b/pkgs/bw-pass/bw-pass index 0e974b7..124714a 100755 --- a/pkgs/bw-pass/bw-pass +++ b/pkgs/bw-pass/bw-pass @@ -66,7 +66,7 @@ query_password() { printf '%s\n' "$PASSWORD" } -if [ $# -lt 1 ] || [ $# -gt 2 ]; then +if [ $# -lt 1 ] || [ $# -gt 2 ]; then usage exit 1 fi diff --git a/pkgs/cgt-calc/default.nix b/pkgs/cgt-calc/default.nix new file mode 100644 index 0000000..9966944 --- /dev/null +++ b/pkgs/cgt-calc/default.nix @@ -0,0 +1,47 @@ +{ lib +, fetchFromGitHub +, python3Packages +, withTeXLive ? true +, texliveSmall +}: +python3Packages.buildPythonApplication rec { + pname = "cgt-calc"; + version = "1.13.0"; + pyproject = true; + + src = fetchFromGitHub { + owner = "KapJI"; + repo = "capital-gains-calculator"; + rev = "v${version}"; + hash = "sha256-y/Y05wG89nccXyxfjqazyPJhd8dOkfwRJre+Rzx97Hw="; + }; + + build-system = with python3Packages; [ + poetry-core + ]; + + dependencies = with python3Packages; [ + defusedxml + jinja2 + pandas + requests + types-requests + yfinance + ]; + + makeWrapperArgs = lib.optionals withTeXLive [ + "--prefix" + "PATH" + ":" + "${lib.getBin texliveSmall}/bin" + ]; + + meta = with lib; { + description = "UK capital gains tax calculator"; + homepage = "https://github.com/KapJI/capital-gains-calculator"; + license = with licenses; [ mit ]; + mainProgram = "cgt-calc"; + maintainers = with maintainers; [ ambroisie ]; + platforms = platforms.unix; + }; +} diff --git a/pkgs/change-audio/change-audio b/pkgs/change-audio/change-audio index 5a1fb9c..612fecf 100755 --- a/pkgs/change-audio/change-audio +++ b/pkgs/change-audio/change-audio @@ -62,7 +62,7 @@ do_toggle() { } case "$1" in - up | down) + up|down) do_change_volume "$@" ;; toggle) diff --git a/pkgs/comma/comma b/pkgs/comma/comma index b03a7f2..4367a26 100755 --- a/pkgs/comma/comma +++ b/pkgs/comma/comma @@ -12,9 +12,9 @@ usage() { find_program() { local CANDIDATE - CANDIDATE="$(nix-locate --minimal --at-root --whole-name "/bin/$1")" + CANDIDATE="$(nix-locate --top-level --minimal --at-root --whole-name "/bin/$1")" if [ "$(printf '%s\n' "$CANDIDATE" | wc -l)" -gt 1 ]; then - CANDIDATE="$(printf '%s' "$CANDIDATE" | "${COMMA_PICKER:-fzf-tmux}")" + CANDIDATE="$(printf '%s' "$CANDIDATE" | fzf-tmux)" fi printf '%s' "$CANDIDATE" } diff --git a/pkgs/default.nix b/pkgs/default.nix index 6b7fce1..949bcf7 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -2,6 +2,8 @@ pkgs.lib.makeScope pkgs.newScope (pkgs: { bw-pass = pkgs.callPackage ./bw-pass { }; + cgt-calc = pkgs.callPackage ./cgt-calc { }; + change-audio = pkgs.callPackage ./change-audio { }; change-backlight = pkgs.callPackage ./change-backlight { }; diff --git a/pkgs/diff-flake/diff-flake b/pkgs/diff-flake/diff-flake index a2a3513..0572b4e 100755 --- a/pkgs/diff-flake/diff-flake +++ b/pkgs/diff-flake/diff-flake @@ -81,23 +81,23 @@ parse_args() { shift case "$opt" in - -h | --help) + -h|--help) usage exit ;; - -f | --flake-output) + -f|--flake-output) FLAKE_OUTPUTS+=("$1") shift ;; - -o | --output) + -o|--output) OUTPUT_FILE="$1" shift ;; - -n | --new-rev) + -n|--new-rev) NEW_REV="$(git rev-parse "$1")" shift ;; - -p | --previous-rev) + -p|--previous-rev) PREVIOUS_REV="$(git rev-parse "$1")" shift ;; @@ -157,7 +157,7 @@ list_dev_shells() { } diff_output() { - local PREV NEW + local PREV NEW; PREV="$(mktemp --dry-run)" NEW="$(mktemp --dry-run)" @@ -169,7 +169,7 @@ diff_output() { printf 'Closure diff for `%s`:\n```\n' "$1" nix store diff-closures "$PREV" "$NEW" | sanitize_output printf '```\n\n' - } >>"$OUTPUT_FILE" + } >> "$OUTPUT_FILE" } parse_args "$@" diff --git a/pkgs/lohr/default.nix b/pkgs/lohr/default.nix index d8545e0..b89ccff 100644 --- a/pkgs/lohr/default.nix +++ b/pkgs/lohr/default.nix @@ -10,7 +10,7 @@ rustPlatform.buildRustPackage rec { hash = "sha256-dunQgtap+XCK5LoSyOqIY/6p6HizBeiyPWNuCffwjDU="; }; - cargoHash = "sha256-R3/N/43+bGx6acE/rhBcrk6kS5zQu8NJ1sVvKJJkK9w="; + cargoHash = "sha256-EUhyrhPe+mUgMmm4o+bxRIiSNReJRfw+/O1fPr8r7lo="; meta = with lib; { description = "Git mirroring daemon"; diff --git a/pkgs/osc52/osc52 b/pkgs/osc52/osc52 index de3a982..f64ccb6 100755 --- a/pkgs/osc52/osc52 +++ b/pkgs/osc52/osc52 @@ -15,7 +15,7 @@ usage() { exec 1>&2 fi - cat <&2 fi - cat < Send a notification (title and message) to the host system using the OSC 777 escape sequence: diff --git a/templates/c++-cmake/.envrc b/templates/c++-cmake/.envrc old mode 100755 new mode 100644 index 390d06d..de77fcb --- a/templates/c++-cmake/.envrc +++ b/templates/c++-cmake/.envrc @@ -1,4 +1,3 @@ -# shellcheck shell=bash if ! has nix_direnv_version || ! nix_direnv_version 3.0.0; then source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.0/direnvrc" "sha256-21TMnI2xWX7HkSTjFFri2UaohXVj854mgvWapWrxRXg=" fi diff --git a/templates/c++-cmake/flake.nix b/templates/c++-cmake/flake.nix index 7796f5e..db3b35c 100644 --- a/templates/c++-cmake/flake.nix +++ b/templates/c++-cmake/flake.nix @@ -16,18 +16,19 @@ ref = "nixos-unstable"; }; - git-hooks = { + pre-commit-hooks = { type = "github"; owner = "cachix"; - repo = "git-hooks.nix"; + repo = "pre-commit-hooks.nix"; ref = "master"; inputs = { + flake-utils.follows = "futils"; nixpkgs.follows = "nixpkgs"; }; }; }; - outputs = { self, futils, nixpkgs, git-hooks }: + outputs = { self, futils, nixpkgs, pre-commit-hooks }: { overlays = { default = final: _prev: { @@ -68,7 +69,7 @@ ]; }; - pre-commit = git-hooks.lib.${system}.run { + pre-commit = pre-commit-hooks.lib.${system}.run { src = self; hooks = { @@ -91,12 +92,12 @@ devShells = { default = pkgs.mkShell { - inputsFrom = [ - self.packages.${system}.project + inputsFrom = with self.packages.${system}; [ + project ]; packages = with pkgs; [ - self.checks.${system}.pre-commit.enabledPackages + clang-tools ]; inherit (pre-commit) shellHook; diff --git a/templates/c++-meson/.envrc b/templates/c++-meson/.envrc index 390d06d..de77fcb 100644 --- a/templates/c++-meson/.envrc +++ b/templates/c++-meson/.envrc @@ -1,4 +1,3 @@ -# shellcheck shell=bash if ! has nix_direnv_version || ! nix_direnv_version 3.0.0; then source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.0/direnvrc" "sha256-21TMnI2xWX7HkSTjFFri2UaohXVj854mgvWapWrxRXg=" fi diff --git a/templates/c++-meson/flake.nix b/templates/c++-meson/flake.nix index cb14eb5..5957c62 100644 --- a/templates/c++-meson/flake.nix +++ b/templates/c++-meson/flake.nix @@ -16,18 +16,19 @@ ref = "nixos-unstable"; }; - git-hooks = { + pre-commit-hooks = { type = "github"; owner = "cachix"; - repo = "git-hooks.nix"; + repo = "pre-commit-hooks.nix"; ref = "master"; inputs = { + flake-utils.follows = "futils"; nixpkgs.follows = "nixpkgs"; }; }; }; - outputs = { self, futils, nixpkgs, git-hooks }: + outputs = { self, futils, nixpkgs, pre-commit-hooks }: { overlays = { default = final: _prev: { @@ -68,7 +69,7 @@ ]; }; - pre-commit = git-hooks.lib.${system}.run { + pre-commit = pre-commit-hooks.lib.${system}.run { src = self; hooks = { @@ -91,12 +92,12 @@ devShells = { default = pkgs.mkShell { - inputsFrom = [ - self.packages.${system}.project + inputsFrom = with self.packages.${system}; [ + project ]; packages = with pkgs; [ - self.checks.${system}.pre-commit.enabledPackages + clang-tools ]; inherit (pre-commit) shellHook; diff --git a/templates/default.nix b/templates/default.nix index 51864cd..44db753 100644 --- a/templates/default.nix +++ b/templates/default.nix @@ -7,10 +7,6 @@ path = ./c++-meson; description = "A C++ project using Meson"; }; - "python-uv" = { - path = ./python-uv; - description = "A Python project using uv"; - }; "rust-cargo" = { path = ./rust-cargo; description = "A Rust project using Cargo"; diff --git a/templates/python-uv/.envrc b/templates/python-uv/.envrc deleted file mode 100644 index 390d06d..0000000 --- a/templates/python-uv/.envrc +++ /dev/null @@ -1,6 +0,0 @@ -# shellcheck shell=bash -if ! has nix_direnv_version || ! nix_direnv_version 3.0.0; then - source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.0/direnvrc" "sha256-21TMnI2xWX7HkSTjFFri2UaohXVj854mgvWapWrxRXg=" -fi - -use flake diff --git a/templates/python-uv/.gitignore b/templates/python-uv/.gitignore deleted file mode 100644 index c79d1e8..0000000 --- a/templates/python-uv/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Virtual environments -.venv - -# Nix generated files -/.pre-commit-config.yaml -/result diff --git a/templates/python-uv/.woodpecker/check.yml b/templates/python-uv/.woodpecker/check.yml deleted file mode 100644 index 272c0e4..0000000 --- a/templates/python-uv/.woodpecker/check.yml +++ /dev/null @@ -1,31 +0,0 @@ -labels: - backend: local - -steps: -- name: pre-commit check - image: bash - commands: - - nix develop --command pre-commit run --all - -- name: nix flake check - image: bash - commands: - - nix flake check - -- name: notify - image: bash - environment: - ADDRESS: - from_secret: matrix_homeserver - ROOM: - from_secret: matrix_roomid - USER: - from_secret: matrix_username - PASS: - from_secret: matrix_password - commands: - - nix run github:ambroisie/matrix-notifier - when: - status: - - failure - - success diff --git a/templates/python-uv/flake.nix b/templates/python-uv/flake.nix deleted file mode 100644 index 5059e64..0000000 --- a/templates/python-uv/flake.nix +++ /dev/null @@ -1,112 +0,0 @@ -{ - description = "A Python project"; - - inputs = { - futils = { - type = "github"; - owner = "numtide"; - repo = "flake-utils"; - ref = "main"; - }; - - nixpkgs = { - type = "github"; - owner = "NixOS"; - repo = "nixpkgs"; - ref = "nixos-unstable"; - }; - - git-hooks = { - type = "github"; - owner = "cachix"; - repo = "git-hooks.nix"; - ref = "master"; - inputs = { - nixpkgs.follows = "nixpkgs"; - }; - }; - }; - - outputs = { self, futils, nixpkgs, git-hooks }: - { - overlays = { - default = final: _prev: { - project = with final; python3.pkgs.buildPythonApplication { - pname = "project"; - version = (final.lib.importTOML ./pyproject.toml).project.version; - pyproject = true; - - src = self; - - build-system = with python3.pkgs; [ setuptools ]; - - pythonImportsCheck = [ "project" ]; - - meta = with lib; { - description = "A Python project"; - homepage = "https://git.belanyi.fr/ambroisie/project"; - license = licenses.mit; - maintainers = with maintainers; [ ambroisie ]; - }; - }; - }; - }; - } // futils.lib.eachDefaultSystem (system: - let - pkgs = import nixpkgs { - inherit system; - overlays = [ - self.overlays.default - ]; - }; - - pre-commit = git-hooks.lib.${system}.run { - src = self; - - hooks = { - mypy = { - enable = true; - }; - - nixpkgs-fmt = { - enable = true; - }; - - ruff = { - enable = true; - }; - - ruff-format = { - enable = true; - }; - }; - }; - in - { - checks = { - inherit (self.packages.${system}) project; - - inherit pre-commit; - }; - - devShells = { - default = pkgs.mkShell { - inputsFrom = [ - self.packages.${system}.project - ]; - - packages = with pkgs; [ - uv - self.checks.${system}.pre-commit.enabledPackages - ]; - - inherit (pre-commit) shellHook; - }; - }; - - packages = futils.lib.flattenTree { - default = pkgs.project; - inherit (pkgs) project; - }; - }); -} diff --git a/templates/python-uv/pyproject.toml b/templates/python-uv/pyproject.toml deleted file mode 100644 index 7b2d896..0000000 --- a/templates/python-uv/pyproject.toml +++ /dev/null @@ -1,17 +0,0 @@ -[build-system] -requires = ["setuptools"] -build-backend = "setuptools.build_meta" - - -[project] -name = "project" -version = "0.0.0" -description = "project description" -requires-python = ">=3.12" -dependencies = [] - -[project.scripts] -project = "project:main" - -[dependency-groups] -dev = [] diff --git a/templates/python-uv/src/project/__init__.py b/templates/python-uv/src/project/__init__.py deleted file mode 100644 index b06117d..0000000 --- a/templates/python-uv/src/project/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -def main() -> None: - print("Hello, world!") diff --git a/templates/rust-cargo/.envrc b/templates/rust-cargo/.envrc index 390d06d..de77fcb 100644 --- a/templates/rust-cargo/.envrc +++ b/templates/rust-cargo/.envrc @@ -1,4 +1,3 @@ -# shellcheck shell=bash if ! has nix_direnv_version || ! nix_direnv_version 3.0.0; then source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.0/direnvrc" "sha256-21TMnI2xWX7HkSTjFFri2UaohXVj854mgvWapWrxRXg=" fi diff --git a/templates/rust-cargo/flake.nix b/templates/rust-cargo/flake.nix index efd8358..6d50369 100644 --- a/templates/rust-cargo/flake.nix +++ b/templates/rust-cargo/flake.nix @@ -16,18 +16,19 @@ ref = "nixos-unstable"; }; - git-hooks = { + pre-commit-hooks = { type = "github"; owner = "cachix"; - repo = "git-hooks.nix"; + repo = "pre-commit-hooks.nix"; ref = "master"; inputs = { + flake-utils.follows = "futils"; nixpkgs.follows = "nixpkgs"; }; }; }; - outputs = { self, futils, nixpkgs, git-hooks }: + outputs = { self, futils, nixpkgs, pre-commit-hooks }: { overlays = { default = final: _prev: { @@ -59,7 +60,7 @@ ]; }; - pre-commit = git-hooks.lib.${system}.run { + pre-commit = pre-commit-hooks.lib.${system}.run { src = self; hooks = { @@ -87,13 +88,14 @@ devShells = { default = pkgs.mkShell { - inputsFrom = [ - self.packages.${system}.project + inputsFrom = with self.packages.${system}; [ + project ]; packages = with pkgs; [ + clippy rust-analyzer - self.checks.${system}.pre-commit.enabledPackages + rustfmt ]; RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";