refactor(nix): dedupe check boilerplate with mkCheck; add meta to tui/web

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.
This commit is contained in:
alt-glitch 2026-07-22 21:35:16 +05:30
parent cbc1054e23
commit fa7937fc64
3 changed files with 40 additions and 63 deletions

View file

@ -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
'';
};
};

View file

@ -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;
};
})

View file

@ -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;
};
})