{ description = "Flake of https://dev.stupstech.de/Mr_Steppy/multi-ssh"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; rust-overlay.url = "github:oxalica/rust-overlay"; }; outputs = { self, nixpkgs, rust-overlay, ... }: let systems = nixpkgs.lib.systems.flakeExposed; forAllSystems = nixpkgs.lib.genAttrs systems; in { packages = forAllSystems (system: let overlays = [ rust-overlay.overlays.default ]; pkgs = import nixpkgs { inherit system; overlays = overlays; }; rustToolchain = pkgs.rust-bin.stable."1.90.0".minimal; rustPlatform = pkgs.makeRustPlatform { cargo = rustToolchain; rustc = rustToolchain; }; in { multiSsh = rustPlatform.buildRustPackage { pname = "multi-ssh"; version = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package.version; src = ./.; cargoLock = { lockFile = ./Cargo.lock; }; nativeCheckInputs = [ pkgs.python3 ]; release = true; }; default = self.packages.${system}.multiSsh; } ); apps = forAllSystems (system: let pkg = self.packages.${system}.multiSsh; in { default = { type = "app"; program = "${pkg}/bin/multi-ssh"; }; } ); nixosModules.multiSsh = { lib, pkgs, config, ... }: { options = { programs.multiSsh = { enable = lib.mkEnableOption "multi-ssh"; editor = lib.mkOption { type = lib.types.nullOr lib.types.str; description = "Editor to forward to multi-ssh (MSSH_EDITOR). If null, the variable is not set."; default = null; }; package = lib.mkOption { type = lib.types.package; description = "Derivation to install for multi-ssh"; default = self.packages.${pkgs.system}.multiSsh; }; servers = { local = { dir = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = ''Local server directory, relative to the user's HOME. If set, a local entry ".:" is added to MSSH_SERVERS.''; }; }; server = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: { options = { dir = lib.mkOption { type = lib.types.str; description = "Server directory for ${name} (remote ssh host), as a string."; }; }; })); default = {}; description = "Attribute set of servers where each attribute name is the ssh host and its value provides a 'dir' string."; }; }; msshAlias = lib.mkOption { type = lib.types.bool; default = true; description = "If true, install an 'mssh' wrapper (alias) that calls 'multi-ssh' and sets env vars."; }; }; }; config = lib.mkIf config.programs.multiSsh.enable (let # Build MSSH_SERVERS from options serverEntries = lib.mapAttrsToList (name: value: "${name}:${value.dir}") config.programs.multiSsh.servers.server; localEntry = lib.optional (config.programs.multiSsh.servers.local.dir != null) ".:${config.programs.multiSsh.servers.local.dir}"; msshServers = lib.concatStringsSep "," (localEntry ++ serverEntries); # wrapper that sets env and execs the real binary (installed as 'multi-ssh') multiSshWrapper = pkgs.writeShellScriptBin "multi-ssh" '' #!${pkgs.runtimeShell} ${lib.optionalString (config.programs.multiSsh.editor != null) ''export MSSH_EDITOR="${config.programs.multiSsh.editor}"''} ${lib.optionalString (msshServers != "") ''export MSSH_SERVERS="${msshServers}"''} exec ${config.programs.multiSsh.package}/bin/multi-ssh "$@" ''; # optional 'mssh' alias that delegates to the wrapped 'multi-ssh' msshWrapper = pkgs.writeShellScriptBin "mssh" '' #!${pkgs.runtimeShell} exec ${multiSshWrapper}/bin/multi-ssh "$@" ''; in { environment.systemPackages = [ multiSshWrapper ] ++ lib.optional config.programs.multiSsh.msshAlias msshWrapper; }); }; }; }