diff --git a/flake.lock b/flake.lock index 9e61219..65b8f04 100644 --- a/flake.lock +++ b/flake.lock @@ -175,11 +175,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1744174375, - "narHash": "sha256-oxI9TLgnQbQ/WL0tIwVSIooLbXq4PW1QUhf5aQmXFgk=", + "lastModified": 1744777043, + "narHash": "sha256-O6jgTxz9BKUiaJl03JsVHvSjtCOC8gHfDvC2UCfcLMc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ef3a956f697525883b77192cbe208233ea0f8f79", + "rev": "7a6f7f4c1c69eee05641beaa40e7f85da8e69fb0", "type": "github" }, "original": { diff --git a/hosts/nixos/porthos/secrets/secrets.nix b/hosts/nixos/porthos/secrets/secrets.nix index 425756c..b3812b4 100644 --- a/hosts/nixos/porthos/secrets/secrets.nix +++ b/hosts/nixos/porthos/secrets/secrets.nix @@ -81,6 +81,7 @@ 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" = { owner = "nginx-sso"; diff --git a/hosts/nixos/porthos/secrets/servarr/cross-seed/configuration.json.age b/hosts/nixos/porthos/secrets/servarr/cross-seed/configuration.json.age new file mode 100644 index 0000000..e9af03f Binary files /dev/null and b/hosts/nixos/porthos/secrets/servarr/cross-seed/configuration.json.age differ diff --git a/hosts/nixos/porthos/services.nix b/hosts/nixos/porthos/services.nix index 561da27..96f15d3 100644 --- a/hosts/nixos/porthos/services.nix +++ b/hosts/nixos/porthos/services.nix @@ -148,6 +148,9 @@ in autobrr = { sessionSecretFile = secrets."servarr/autobrr/session-secret".path; }; + cross-seed = { + secretSettingsFile = secrets."servarr/cross-seed/configuration.json".path; + }; # ... But not Lidarr because I don't care for music that much lidarr = { enable = false; diff --git a/modules/home/atuin/default.nix b/modules/home/atuin/default.nix index 3f06263..dbd9690 100644 --- a/modules/home/atuin/default.nix +++ b/modules/home/atuin/default.nix @@ -8,6 +8,10 @@ in # I want the full experience by default package = mkPackageOption pkgs "atuin" { }; + + daemon = { + enable = my.mkDisableOption "atuin daemon"; + }; }; config = lib.mkIf cfg.enable { @@ -15,12 +19,18 @@ 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 c8183cf..4dbd510 100644 --- a/modules/home/default.nix +++ b/modules/home/default.nix @@ -8,6 +8,7 @@ ./bluetooth ./calibre ./comma + ./delta ./dircolors ./direnv ./discord @@ -23,6 +24,7 @@ ./gtk ./htop ./jq + ./jujutsu ./keyboard ./mail ./mpv diff --git a/modules/home/delta/default.nix b/modules/home/delta/default.nix new file mode 100644 index 0000000..6970b78 --- /dev/null +++ b/modules/home/delta/default.nix @@ -0,0 +1,92 @@ +{ 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"; + }; + + jujutsu = { + enable = my.mkDisableOption "jujutsu integration"; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [ + { + # For its configuration + assertion = cfg.enable -> cfg.git.enable; + message = '' + `config.my.home.delta` must enable `config.my.home.delta.git` to be + properly configured. + ''; + } + { + assertion = cfg.jujutsu.enable -> cfg.git.enable; + message = '' + `config.my.home.delta.jujutsu` relies on `config.my.home.delta.git` + being enabled. + ''; + } + ]; + + home.packages = [ cfg.package ]; + + programs.git = lib.mkIf cfg.git.enable { + delta = { + enable = true; + inherit (cfg) package; + + 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"; + }; + }; + }; + }; + + programs.jujutsu = lib.mkIf cfg.jujutsu.enable { + settings = { + merge-tools = { + delta = { + # Errors are signaled with exit codes greater or equal to 2 + diff-expected-exit-codes = [ 0 1 ]; + }; + }; + + ui = { + diff = { + # Delta expects a `git diff` input + format = "git"; + }; + + pager = "delta"; + }; + }; + }; + }; +} diff --git a/modules/home/git/default.nix b/modules/home/git/default.nix index c88008f..ca59a5f 100644 --- a/modules/home/git/default.nix +++ b/modules/home/git/default.nix @@ -42,34 +42,6 @@ in 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 extraConfig = { # Makes it a bit more readable diff --git a/modules/home/jujutsu/default.nix b/modules/home/jujutsu/default.nix new file mode 100644 index 0000000..2cf6f64 --- /dev/null +++ b/modules/home/jujutsu/default.nix @@ -0,0 +1,128 @@ +{ config, pkgs, lib, ... }: +let + cfg = config.my.home.jujutsu; + + inherit (lib.my) mkMailAddress; +in +{ + options.my.home.jujutsu = with lib; { + enable = my.mkDisableOption "jujutsu configuration"; + + package = mkPackageOption pkgs "jujutsu" { }; + }; + + config = lib.mkIf cfg.enable { + assertions = [ + { + # For `jj git` commands + assertion = cfg.enable -> config.my.home.git.enable; + message = '' + `config.my.home.jujutsu` relies on `config.my.home.git` being enabled. + ''; + } + ]; + + programs.jujutsu = { + enable = true; + + inherit (cfg) package; + + settings = { + # Who am I? + user = { + name = "Bruno BELANYI"; + email = mkMailAddress "bruno" "belanyi.fr"; + }; + + aliases = { + jj = [ "util" "exec" "--" "jj" ]; + # FIXME: + # * topo sort by default (I think? test it) + # * still not a big fan of the template + lol = [ "log" "-r" "..@" "-T" "builtin_log_oneline" ]; + lola = [ "lol" "-r" "all()" ]; + # TODO: + # * `pick` (https://github.com/jj-vcs/jj/issues/5446): [ "util" "exec" "--" "bash" "-c" "jj log -p -r \"diff_contains($1)\"" ] + # * `root`: `jj workspace root` (barely necessary then) + }; + + # FIXME: git equivalents + # blame = { + # coloring = "repeatedLines"; + # markIgnoredLines = true; + # markUnblamables = true; + # }; + # FIXME: log colors should probably match git + # FIXME: patience diff? + # FIXME: fetch prune/pruneTags? + # FIXME: pull.rebase=true? Probably true TBH + # FIXME: push.default=simple? Probably true TBH + # FIXME: conflict style? ui.conflict-marker-style=git is diff3/zdiff3 + + # FIXME: from ma_9's config, plus my own stuff + # snapshot = { + # auto-track = "none()"; + # }; + # + # ui = { + # diff-editor = ":builtin"; # To silence hints + # movement = { + # edit = false; + # }; + # }; + + templates = { + # Equivalent to `commit.verbose = true` in Git + draft_commit_description = "commit_description_verbose(self)"; + }; + + template-aliases = { + "commit_description_verbose(commit)" = '' + concat( + commit_description(commit), + "JJ: ignore-rest\n", + diff.git(), + ) + ''; + # FIXME: use `diff.summary()` instead? Supported by syntax highlighting + # See https://github.com/jj-vcs/jj/issues/1946#issuecomment-2572986485 + # FIXME: tree-sitter grammar isn't in `nvim-treesitter` (https://github.com/kareigu/tree-sitter-jjdescription) + "commit_description(commit)" = '' + concat( + commit.description(), "\n", + "JJ: This commit contains the following changes:\n", + indent("JJ: ", diff.stat(72)), + ) + ''; + }; + + "--scope" = [ + # Multiple identities + { + "--when" = { + repositories = [ "~/git/EPITA/" ]; + }; + user = { + name = "Bruno BELANYI"; + email = mkMailAddress "bruno.belanyi" "epita.fr"; + }; + } + { + "--when" = { + repositories = [ "~/git/work/" ]; + }; + user = { + name = "Bruno BELANYI"; + email = mkMailAddress "ambroisie" "google.com"; + }; + } + ]; + }; + }; + + # To drop in a `local.toml` configuration, not-versioned + xdg.configFile = { + "jj/conf.d/.keep".text = ""; + }; + }; +} diff --git a/modules/home/tmux/default.nix b/modules/home/tmux/default.nix index 08b9202..82ceb3a 100644 --- a/modules/home/tmux/default.nix +++ b/modules/home/tmux/default.nix @@ -6,7 +6,7 @@ let (config.my.home.wm.windowManager != null) ]; - mkTerminalFlags = opt: flag: + mkTerminalFeature = opt: flag: let mkFlag = term: ''set -as terminal-features ",${term}:${flag}"''; enabledTerminals = lib.filterAttrs (_: v: v.${opt}) cfg.terminalFeatures; @@ -123,9 +123,9 @@ in } # Force OSC8 hyperlinks for each relevant $TERM - ${mkTerminalFlags "hyperlinks" "hyperlinks"} + ${mkTerminalFeature "hyperlinks" "hyperlinks"} # Force 24-bit color for each relevant $TERM - ${mkTerminalFlags "trueColor" "RGB"} + ${mkTerminalFeature "trueColor" "RGB"} ''; }; } diff --git a/modules/home/vim/after/queries/gitcommit/highlights.scm b/modules/home/vim/after/queries/gitcommit/highlights.scm new file mode 100644 index 0000000..05162c9 --- /dev/null +++ b/modules/home/vim/after/queries/gitcommit/highlights.scm @@ -0,0 +1,6 @@ +; 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/plugin/numbertoggle.lua b/modules/home/vim/plugin/numbertoggle.lua index 8042710..b1e3df2 100644 --- a/modules/home/vim/plugin/numbertoggle.lua +++ b/modules/home/vim/plugin/numbertoggle.lua @@ -22,13 +22,3 @@ 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/signtoggle.lua b/modules/home/vim/plugin/signtoggle.lua index 9765a81..3deca34 100644 --- a/modules/home/vim/plugin/signtoggle.lua +++ b/modules/home/vim/plugin/signtoggle.lua @@ -1,26 +1,21 @@ local signtoggle = vim.api.nvim_create_augroup("signtoggle", { clear = true }) --- Only show sign column for the currently focused buffer +-- Only show sign column for the currently focused buffer, if it has a number column vim.api.nvim_create_autocmd({ "BufEnter", "FocusGained", "WinEnter" }, { pattern = "*", group = signtoggle, callback = function() - vim.opt.signcolumn = "yes" + if vim.opt.number:get() then + vim.opt.signcolumn = "yes" + end end, }) vim.api.nvim_create_autocmd({ "BufLeave", "FocusLost", "WinLeave" }, { pattern = "*", group = signtoggle, callback = function() - 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" + if vim.opt.number:get() then + vim.opt.signcolumn = "no" + end end, }) diff --git a/modules/nixos/services/servarr/autobrr.nix b/modules/nixos/services/servarr/autobrr.nix index afb07f4..c3370cb 100644 --- a/modules/nixos/services/servarr/autobrr.nix +++ b/modules/nixos/services/servarr/autobrr.nix @@ -1,4 +1,4 @@ -# IRC-based +# IRC-based indexer { config, lib, ... }: let cfg = config.my.services.servarr.autobrr; @@ -40,6 +40,7 @@ in my.services.nginx.virtualHosts = { autobrr = { inherit (cfg) port; + websocketsLocations = [ "/api" ]; }; }; @@ -54,7 +55,7 @@ in environment.etc = { "fail2ban/filter.d/autobrr.conf".text = '' [Definition] - failregex = ^.*Auth: invalid login \[.*\] from: $ + failregex = "message":"Auth: Failed login attempt username: \[.*\] ip: " journalmatch = _SYSTEMD_UNIT=autobrr.service ''; }; diff --git a/modules/nixos/services/servarr/cross-seed.nix b/modules/nixos/services/servarr/cross-seed.nix new file mode 100644 index 0000000..74f216a --- /dev/null +++ b/modules/nixos/services/servarr/cross-seed.nix @@ -0,0 +1,96 @@ +# 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 409fcdc..dca57cf 100644 --- a/modules/nixos/services/servarr/default.nix +++ b/modules/nixos/services/servarr/default.nix @@ -7,6 +7,7 @@ imports = [ ./autobrr.nix ./bazarr.nix + ./cross-seed.nix ./jackett.nix ./nzbhydra.nix ./prowlarr.nix diff --git a/modules/nixos/services/transmission/default.nix b/modules/nixos/services/transmission/default.nix index ac8b24d..16d51e3 100644 --- a/modules/nixos/services/transmission/default.nix +++ b/modules/nixos/services/transmission/default.nix @@ -65,6 +65,8 @@ in # Proxied behind Nginx. rpc-whitelist-enabled = true; rpc-whitelist = "127.0.0.1"; + + umask = "002"; # To go with `downloadDirPermissions` }; };