From fa7937fc64972cf6349dd60b5da41b8307f2e684 Mon Sep 17 00:00:00 2001 From: alt-glitch Date: Wed, 22 Jul 2026 21:35:16 +0530 Subject: [PATCH] refactor(nix): dedupe check boilerplate with mkCheck; add meta to tui/web MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every check hand-rolled the same 'set -e' prelude and $out success stamp; a small mkCheck helper now owns that shape (checks that don't fit, like the eval-only ones, stay as plain runCommand). packages.tui and packages.web are user-facing flake outputs but carried no meta at all — add description/homepage/license/platforms matching the style of hermes-agent.nix and desktop.nix. --- nix/checks.nix | 85 ++++++++++++++------------------------------------ nix/tui.nix | 9 +++++- nix/web.nix | 9 +++++- 3 files changed, 40 insertions(+), 63 deletions(-) diff --git a/nix/checks.nix b/nix/checks.nix index 7f625ca4fe4b..2260f43a4b74 100644 --- a/nix/checks.nix +++ b/nix/checks.nix @@ -11,6 +11,14 @@ configMergeScript = pkgs.callPackage ./configMergeScript.nix { }; + mkCheck = name: attrs: script: + pkgs.runCommand name attrs ('' + set -e + '' + script + '' + mkdir -p $out + echo "ok" > $out/result + ''); + # Auto-generated config key reference — always in sync with Python configKeys = pkgs.runCommand "hermes-config-keys" {} '' set -euo pipefail @@ -49,35 +57,28 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) results = map (sys: { inherit sys; result = tryEvalPkg sys; }) targetSystems; failures = builtins.filter (r: !r.result.success) results; failMsg = lib.concatMapStringsSep "\n" (r: " - ${r.sys}") failures; - in pkgs.runCommand "hermes-cross-eval" { } ( + in mkCheck "hermes-cross-eval" { } ( if failures != [] then throw "Package fails to evaluate on:\n${failMsg}" else '' echo "PASS: package evaluates on all ${toString (builtins.length targetSystems)} platforms" - mkdir -p $out - echo "ok" > $out/result '' ); # Verify the default package builds successfully (cross-platform). # On Linux the runtime checks below already depend on the package, # but this ensures darwin builders also build it during flake check. - build-package = pkgs.runCommand "hermes-build-package" { } '' + build-package = mkCheck "hermes-build-package" { } '' echo "PASS: package built at ${hermes-agent}" - mkdir -p $out - echo "ok" > $out/result ''; # Verify the devShell builds successfully (cross-platform). - build-devshell = pkgs.runCommand "hermes-build-devshell" { } '' + build-devshell = mkCheck "hermes-build-devshell" { } '' echo "PASS: devShell built at ${self'.devShells.default}" - mkdir -p $out - echo "ok" > $out/result ''; } // lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { # Verify binaries exist and are executable - package-contents = pkgs.runCommand "hermes-package-contents" { } '' - set -e + package-contents = mkCheck "hermes-package-contents" { } '' echo "=== Checking binaries ===" test -x ${hermes-agent}/bin/hermes || (echo "FAIL: hermes binary missing"; exit 1) test -x ${hermes-agent}/bin/hermes-agent || (echo "FAIL: hermes-agent binary missing"; exit 1) @@ -88,26 +89,20 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) echo "PASS: Version check" echo "=== All checks passed ===" - mkdir -p $out - echo "ok" > $out/result ''; # Verify every pyproject.toml [project.scripts] entry has a wrapped binary - entry-points-sync = pkgs.runCommand "hermes-entry-points-sync" { } '' - set -e + entry-points-sync = mkCheck "hermes-entry-points-sync" { } '' echo "=== Checking entry points match pyproject.toml [project.scripts] ===" for bin in hermes hermes-agent hermes-acp; do test -x ${hermes-agent}/bin/$bin || (echo "FAIL: $bin binary missing from Nix package"; exit 1) echo "PASS: $bin present" done - mkdir -p $out - echo "ok" > $out/result ''; # Verify CLI subcommands are accessible - cli-commands = pkgs.runCommand "hermes-cli-commands" { } '' - set -e + cli-commands = mkCheck "hermes-cli-commands" { } '' export HOME=$(mktemp -d) echo "=== Checking hermes --help ===" @@ -116,13 +111,10 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) echo "PASS: All subcommands accessible" echo "=== All CLI checks passed ===" - mkdir -p $out - echo "ok" > $out/result ''; # Verify bundled skills are present in the package - bundled-skills = pkgs.runCommand "hermes-bundled-skills" { } '' - set -e + bundled-skills = mkCheck "hermes-bundled-skills" { } '' echo "=== Checking bundled skills ===" test -d ${hermes-agent}/share/hermes-agent/skills || (echo "FAIL: skills directory missing"; exit 1) echo "PASS: skills directory exists" @@ -147,13 +139,10 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) echo "PASS: $OPT_COUNT optional skills found, HERMES_OPTIONAL_SKILLS set in wrapper" echo "=== All bundled skills checks passed ===" - mkdir -p $out - echo "ok" > $out/result ''; # Verify bundled plugins (platforms, memory, context_engine) are present - bundled-plugins = pkgs.runCommand "hermes-bundled-plugins" { } '' - set -e + bundled-plugins = mkCheck "hermes-bundled-plugins" { } '' echo "=== Checking bundled plugins ===" test -d ${hermes-agent}/share/hermes-agent/plugins || (echo "FAIL: plugins directory missing"; exit 1) echo "PASS: plugins directory exists" @@ -167,15 +156,12 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) echo "PASS: HERMES_BUNDLED_PLUGINS set in wrapper" echo "=== All bundled plugins checks passed ===" - mkdir -p $out - echo "ok" > $out/result ''; # Verify bundled i18n locale catalogs are present and resolvable. # Regression for #23943 / #27632 / #35374 — sealed Nix venvs dropped # locales/, surfacing raw i18n keys like gateway.reset.header_default. - bundled-locales = pkgs.runCommand "hermes-bundled-locales" { } '' - set -e + bundled-locales = mkCheck "hermes-bundled-locales" { } '' echo "=== Checking bundled locales ===" test -d ${hermes-agent}/share/hermes-agent/locales || (echo "FAIL: locales directory missing"; exit 1) echo "PASS: locales directory exists" @@ -215,13 +201,10 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) echo "PASS: sealed venv resolves locales via data-files without the env var" echo "=== All bundled locales checks passed ===" - mkdir -p $out - echo "ok" > $out/result ''; # Verify bundled TUI is present and compiled - bundled-tui = pkgs.runCommand "hermes-bundled-tui" { } '' - set -e + bundled-tui = mkCheck "hermes-bundled-tui" { } '' echo "=== Checking bundled TUI ===" test -d ${hermes-agent}/ui-tui || (echo "FAIL: ui-tui directory missing"; exit 1) echo "PASS: ui-tui directory exists" @@ -236,14 +219,11 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) echo "PASS: HERMES_TUI_DIR set in wrapper" echo "=== All bundled TUI checks passed ===" - mkdir -p $out - echo "ok" > $out/result ''; # Verify HERMES_NODE is set in wrapper and points to Node 20+ # (string-width uses the /v regex flag which requires Node 20+) - hermes-node = pkgs.runCommand "hermes-node-version" { } '' - set -e + hermes-node = mkCheck "hermes-node-version" { } '' echo "=== Checking HERMES_NODE in wrapper ===" grep -q "HERMES_NODE" ${hermes-agent}/bin/hermes || \ (echo "FAIL: HERMES_NODE not set in wrapper"; exit 1) @@ -259,13 +239,10 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) echo "PASS: Node v$NODE_MAJOR >= 20" echo "=== All HERMES_NODE checks passed ===" - mkdir -p $out - echo "ok" > $out/result ''; # Verify HERMES_MANAGED guard works on all mutation commands - managed-guard = pkgs.runCommand "hermes-managed-guard" { } '' - set -e + managed-guard = mkCheck "hermes-managed-guard" { } '' export HOME=$(mktemp -d) check_blocked() { @@ -281,8 +258,6 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) check_blocked "config edit" ${hermes-agent}/bin/hermes config edit echo "=== All guard checks passed ===" - mkdir -p $out - echo "ok" > $out/result ''; # Verify extraPythonPackages PYTHONPATH injection @@ -291,8 +266,7 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) hermesWithExtra = hermes-agent.override { extraPythonPackages = [ testPkg ]; }; - in pkgs.runCommand "hermes-extra-python-packages" { } '' - set -e + in mkCheck "hermes-extra-python-packages" { } '' echo "=== Checking extraPythonPackages PYTHONPATH injection ===" grep -q "PYTHONPATH" ${hermesWithExtra}/bin/hermes || \ @@ -310,8 +284,6 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) echo "PASS: base package clean" echo "=== All extraPythonPackages checks passed ===" - mkdir -p $out - echo "ok" > $out/result ''; # Verify extraDependencyGroups passes through to python.nix @@ -319,8 +291,7 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) hermesWithGroups = hermes-agent.override { extraDependencyGroups = [ "honcho" ]; }; - in pkgs.runCommand "hermes-extra-dependency-groups" { } '' - set -e + in mkCheck "hermes-extra-dependency-groups" { } '' echo "=== Checking extraDependencyGroups override evaluates ===" # Eval-only: verify the override produces valid derivation paths @@ -331,21 +302,16 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) echo "PASS: extraDependencyGroups override evaluates cleanly" echo "=== All extraDependencyGroups checks passed ===" - mkdir -p $out - echo "ok" > $out/result ''; # Regression guard: messaging deps live outside [all], so the # #messaging variant must actually ship discord.py — otherwise # `nix profile install .#messaging` regresses to the broken default. - messaging-variant = pkgs.runCommand "hermes-messaging-variant" { } '' - set -e + messaging-variant = mkCheck "hermes-messaging-variant" { } '' echo "=== Checking discord.py importable from messaging variant ===" ${self'.packages.messaging.hermesVenv}/bin/python3 -c \ "import discord; print(discord.__version__)" echo "PASS: discord.py importable from messaging variant venv" - mkdir -p $out - echo "ok" > $out/result ''; # ── Config merge + round-trip test ──────────────────────────────── @@ -408,10 +374,9 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) - USER_VAR ''; - in pkgs.runCommand "hermes-config-roundtrip" { + in mkCheck "hermes-config-roundtrip" { nativeBuildInputs = [ pkgs.jq ]; } '' - set -e export HOME=$(mktemp -d) ERRORS="" @@ -558,8 +523,6 @@ json.dump(load_config(), sys.stdout, default=str) echo "" echo "=== All 7 merge scenarios passed ===" - mkdir -p $out - echo "ok" > $out/result ''; }; }; diff --git a/nix/tui.nix b/nix/tui.nix index c61c6fedf1e5..f481bbe9de14 100644 --- a/nix/tui.nix +++ b/nix/tui.nix @@ -1,5 +1,5 @@ # nix/tui.nix — Hermes TUI (Ink/React) compiled with tsc and bundled -{ pkgs, hermesNpmLib, ... }: +{ pkgs, lib, hermesNpmLib, ... }: let npm = hermesNpmLib.mkNpmPassthru { dirs = [ @@ -35,4 +35,11 @@ pkgs.buildNpmPackage (npm // { runHook postInstall ''; + + meta = with lib; { + description = "Ink/React terminal user interface for Hermes Agent"; + homepage = "https://github.com/NousResearch/hermes-agent"; + license = licenses.mit; + platforms = platforms.unix; + }; }) diff --git a/nix/web.nix b/nix/web.nix index b93466a2a14f..5b5a56e2c995 100644 --- a/nix/web.nix +++ b/nix/web.nix @@ -1,5 +1,5 @@ # nix/web.nix — Hermes Web Dashboard (Vite/React) frontend build -{ pkgs, hermesNpmLib, ... }: +{ pkgs, lib, hermesNpmLib, ... }: let # @hermes/shared ships as a file: workspace dep of web, so its source # must be in the filtered src tree too. @@ -38,4 +38,11 @@ pkgs.buildNpmPackage (npm // { cp -r web/dist $out runHook postInstall ''; + + meta = with lib; { + description = "Web dashboard frontend for Hermes Agent"; + homepage = "https://github.com/NousResearch/hermes-agent"; + license = licenses.mit; + platforms = platforms.unix; + }; })