ci: fix lint glob + format nix files

This commit is contained in:
Ciphercat 2026-03-18 02:24:18 +00:00
commit ba6c63e9bf
4 changed files with 113 additions and 58 deletions

View file

@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@v13 - uses: DeterminateSystems/nix-installer-action@v13
- name: Check formatting (nixfmt) - name: Check formatting (nixfmt)
run: nix run nixpkgs#nixfmt-rfc-style -- --check *.nix docs/*.nix tests/*.nix run: find . -name '*.nix' -not -path './_*' | xargs nix run nixpkgs#nixfmt-rfc-style -- --check
- name: Lint (statix) - name: Lint (statix)
run: nix run nixpkgs#statix -- check . run: nix run nixpkgs#statix -- check .

View file

@ -6,8 +6,14 @@
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
}; };
outputs = { self, nixpkgs, flake-utils }: outputs =
flake-utils.lib.eachDefaultSystem (system: {
self,
nixpkgs,
flake-utils,
}:
flake-utils.lib.eachDefaultSystem (
system:
let let
pkgs = import nixpkgs { inherit system; }; pkgs = import nixpkgs { inherit system; };
in in
@ -19,14 +25,15 @@
checks = import ./checks.nix { checks = import ./checks.nix {
inherit pkgs; inherit pkgs;
hermes-agent = self.packages.${system}.hermes-agent; inherit (self.packages.${system}) hermes-agent;
}; };
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
packages = [ self.packages.${system}.hermes-agent ]; packages = [ self.packages.${system}.hermes-agent ];
}; };
} }
) // { )
// {
nixosModules = { nixosModules = {
hermes-agent = import ./module.nix self; hermes-agent = import ./module.nix self;
default = self.nixosModules.hermes-agent; default = self.nixosModules.hermes-agent;

View file

@ -1,9 +1,14 @@
self: self:
{ config, lib, pkgs, ... }: {
config,
lib,
pkgs,
...
}:
let let
cfg = config.services.hermes-agent; cfg = config.services.hermes-agent;
hermes-agent = self.packages.${pkgs.system}.hermes-agent; inherit (self.packages.${pkgs.system}) hermes-agent;
# Deep-merge config type (same pattern as nix-openclaw) # Deep-merge config type (same pattern as nix-openclaw)
deepConfigType = lib.types.mkOptionType { deepConfigType = lib.types.mkOptionType {
@ -20,20 +25,21 @@ let
configFile = if cfg.configFile != null then cfg.configFile else generatedConfigFile; configFile = if cfg.configFile != null then cfg.configFile else generatedConfigFile;
# Generate .env file from environment attrset (non-secret env vars) # Generate .env file from environment attrset (non-secret env vars)
envFileContent = lib.concatStringsSep "\n" ( envFileContent = lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v: "${k}=${v}") cfg.environment);
lib.mapAttrsToList (k: v: "${k}=${v}") cfg.environment
);
generatedEnvFile = pkgs.writeText "hermes-env" envFileContent; generatedEnvFile = pkgs.writeText "hermes-env" envFileContent;
# Document files → symlinked into workspace # Document files → symlinked into workspace
documentDerivation = pkgs.runCommand "hermes-documents" { } ( documentDerivation = pkgs.runCommand "hermes-documents" { } (
'' ''
mkdir -p $out mkdir -p $out
'' + lib.concatStringsSep "\n" ( ''
lib.mapAttrsToList (name: value: + lib.concatStringsSep "\n" (
if builtins.isPath value || lib.isStorePath value lib.mapAttrsToList (
then "cp ${value} $out/${name}" name: value:
else "cat > $out/${name} <<'HERMES_DOC_EOF'\n${value}\nHERMES_DOC_EOF" if builtins.isPath value || lib.isStorePath value then
"cp ${value} $out/${name}"
else
"cat > $out/${name} <<'HERMES_DOC_EOF'\n${value}\nHERMES_DOC_EOF"
) cfg.documents ) cfg.documents
) )
); );
@ -254,14 +260,28 @@ in
# ── MCP servers ────────────────────────────────────────────────────── # ── MCP servers ──────────────────────────────────────────────────────
mcpServers = mkOption { mcpServers = mkOption {
type = types.attrsOf (types.submodule { type = types.attrsOf (
options = { types.submodule {
command = mkOption { type = types.str; description = "MCP server command."; }; options = {
args = mkOption { type = types.listOf types.str; default = [ ]; }; command = mkOption {
env = mkOption { type = types.attrsOf types.str; default = { }; }; type = types.str;
timeout = mkOption { type = types.nullOr types.int; default = null; }; description = "MCP server command.";
}; };
}); args = mkOption {
type = types.listOf types.str;
default = [ ];
};
env = mkOption {
type = types.attrsOf types.str;
default = { };
};
timeout = mkOption {
type = types.nullOr types.int;
default = null;
};
};
}
);
default = { }; default = { };
description = "MCP server configurations (merged into config.mcp_servers)."; description = "MCP server configurations (merged into config.mcp_servers).";
}; };
@ -270,8 +290,11 @@ in
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
# ── Merge MCP servers into config ──────────────────────────────────── # ── Merge MCP servers into config ────────────────────────────────────
services.hermes-agent.config = lib.mkIf (cfg.mcpServers != { }) { services.hermes-agent.config = lib.mkIf (cfg.mcpServers != { }) {
mcp_servers = lib.mapAttrs (_name: srv: mcp_servers = lib.mapAttrs (
{ inherit (srv) command args; } _name: srv:
{
inherit (srv) command args;
}
// lib.optionalAttrs (srv.env != { }) { inherit (srv) env; } // lib.optionalAttrs (srv.env != { }) { inherit (srv) env; }
// lib.optionalAttrs (srv.timeout != null) { inherit (srv) timeout; } // lib.optionalAttrs (srv.timeout != null) { inherit (srv) timeout; }
) cfg.mcpServers; ) cfg.mcpServers;
@ -281,7 +304,7 @@ in
users.groups.${cfg.group} = lib.mkIf cfg.createUser { }; users.groups.${cfg.group} = lib.mkIf cfg.createUser { };
users.users.${cfg.user} = lib.mkIf cfg.createUser { users.users.${cfg.user} = lib.mkIf cfg.createUser {
isSystemUser = true; isSystemUser = true;
group = cfg.group; inherit (cfg) group;
home = cfg.stateDir; home = cfg.stateDir;
createHome = true; createHome = true;
shell = pkgs.bashInteractive; shell = pkgs.bashInteractive;
@ -302,19 +325,26 @@ in
# Seed auth file if provided (only if not already present, unless force overwrite) # Seed auth file if provided (only if not already present, unless force overwrite)
${lib.optionalString (cfg.authFile != null) '' ${lib.optionalString (cfg.authFile != null) ''
${if cfg.authFileForceOverwrite then '' ${
install -o ${cfg.user} -g ${cfg.group} -m 0600 ${cfg.authFile} ${cfg.stateDir}/.hermes/auth.json if cfg.authFileForceOverwrite then
'' else '' ''
if [ ! -f ${cfg.stateDir}/.hermes/auth.json ]; then install -o ${cfg.user} -g ${cfg.group} -m 0600 ${cfg.authFile} ${cfg.stateDir}/.hermes/auth.json
install -o ${cfg.user} -g ${cfg.group} -m 0600 ${cfg.authFile} ${cfg.stateDir}/.hermes/auth.json ''
fi else
''} ''
if [ ! -f ${cfg.stateDir}/.hermes/auth.json ]; then
install -o ${cfg.user} -g ${cfg.group} -m 0600 ${cfg.authFile} ${cfg.stateDir}/.hermes/auth.json
fi
''
}
''} ''}
# Link documents into workspace # Link documents into workspace
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: _value: '' ${lib.concatStringsSep "\n" (
install -o ${cfg.user} -g ${cfg.group} -m 0644 ${documentDerivation}/${name} ${cfg.workingDirectory}/${name} lib.mapAttrsToList (name: _value: ''
'') cfg.documents)} install -o ${cfg.user} -g ${cfg.group} -m 0644 ${documentDerivation}/${name} ${cfg.workingDirectory}/${name}
'') cfg.documents
)}
''; '';
# ── systemd service ────────────────────────────────────────────────── # ── systemd service ──────────────────────────────────────────────────
@ -335,16 +365,19 @@ in
Group = cfg.group; Group = cfg.group;
WorkingDirectory = cfg.workingDirectory; WorkingDirectory = cfg.workingDirectory;
EnvironmentFile = EnvironmentFile = [ generatedEnvFile ] ++ cfg.environmentFiles;
[ generatedEnvFile ]
++ cfg.environmentFiles;
ExecStart = ExecStart =
if cfg.execStart != null then cfg.execStart if cfg.execStart != null then
else lib.concatStringsSep " " ([ cfg.execStart
"${cfg.package}/bin/hermes" else
"gateway" lib.concatStringsSep " " (
] ++ cfg.extraArgs); [
"${cfg.package}/bin/hermes"
"gateway"
]
++ cfg.extraArgs
);
Restart = cfg.restart; Restart = cfg.restart;
RestartSec = cfg.restartSec; RestartSec = cfg.restartSec;
@ -365,7 +398,8 @@ in
pkgs.bash pkgs.bash
pkgs.coreutils pkgs.coreutils
pkgs.git pkgs.git
] ++ cfg.extraPackages; ]
++ cfg.extraPackages;
}; };
}; };
} }

View file

@ -1,13 +1,14 @@
{ lib {
, python312Packages lib,
, python312 python312Packages,
, fetchFromGitHub python312,
, fetchPypi fetchFromGitHub,
, makeWrapper fetchPypi,
, nodejs_22 makeWrapper,
, ripgrep nodejs_22,
, ffmpeg ripgrep,
, git ffmpeg,
git,
}: }:
let let
@ -25,7 +26,10 @@ let
inherit version; inherit version;
hash = "sha256-nhwH0KYbRSqP+0jBmd5fJUPXVG8SMPYxI3BEMSfF6Tc="; hash = "sha256-nhwH0KYbRSqP+0jBmd5fJUPXVG8SMPYxI3BEMSfF6Tc=";
}; };
build-system = with pythonPackages; [ setuptools setuptools-scm ]; build-system = with pythonPackages; [
setuptools
setuptools-scm
];
dependencies = with pythonPackages; [ dependencies = with pythonPackages; [
httpx httpx
httpx-sse httpx-sse
@ -45,7 +49,10 @@ let
inherit version; inherit version;
hash = "sha256-b97r+UVOYrxSPVeIjlA1nme6r9sh9oYh+cFOCNwAYjo="; hash = "sha256-b97r+UVOYrxSPVeIjlA1nme6r9sh9oYh+cFOCNwAYjo=";
}; };
build-system = with pythonPackages; [ setuptools wheel ]; build-system = with pythonPackages; [
setuptools
wheel
];
dependencies = with pythonPackages; [ dependencies = with pythonPackages; [
httpx httpx
pydantic pydantic
@ -166,7 +173,14 @@ pythonPackages.buildPythonApplication {
for bin in $out/bin/hermes $out/bin/hermes-agent $out/bin/hermes-acp; do for bin in $out/bin/hermes $out/bin/hermes-agent $out/bin/hermes-acp; do
if [ -f "$bin" ]; then if [ -f "$bin" ]; then
wrapProgram "$bin" \ wrapProgram "$bin" \
--prefix PATH : ${lib.makeBinPath [ nodejs_22 ripgrep ffmpeg git ]} --prefix PATH : ${
lib.makeBinPath [
nodejs_22
ripgrep
ffmpeg
git
]
}
fi fi
done done
''; '';