mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
* fix(nix): use --rebuild in fix-lockfiles to bypass cached FOD store paths fix-lockfiles checked npm lockfile hashes by running `nix build .#<attr>.npmDeps`, but fetchNpmDeps is a fixed-output derivation — if the old store path exists locally, Nix returns it from cache without re-fetching. This caused the script to report "ok" even when hashes were stale, while CI (with no cache) failed with a hash mismatch. Adding --rebuild forces Nix to re-derive and verify the output hash against the declared one, catching staleness regardless of local cache state. Also updates the tui and web npm deps hashes that were stale. * fix(nix): regenerate ui-tui lockfile to add missing @emnapi entries npm ci was failing because @emnapi/core and @emnapi/runtime were missing from ui-tui/package-lock.json despite being required as peer deps by @napi-rs/wasm-runtime (via @rolldown/binding-wasm32-wasi). Running npm install --package-lock-only adds the missing entries. The npmDepsHash reverts to its previous value since fetchNpmDeps was already fetching these packages as transitive dependencies.
217 lines
8.1 KiB
Nix
217 lines
8.1 KiB
Nix
# nix/lib.nix — Shared helpers for nix stuff
|
|
{ pkgs, npm-lockfile-fix }:
|
|
{
|
|
# Returns a buildNpmPackage-compatible attrs set that provides:
|
|
# patchPhase — ensures lockfile has exactly one trailing newline
|
|
# nativeBuildInputs — [ updateLockfileScript ] (list, prepend with ++ for more)
|
|
# passthru.devShellHook — stamp-checked npm install + hash auto-update
|
|
# passthru.npmLockfile — metadata for mkFixLockfiles
|
|
#
|
|
# NOTE: npmConfigHook runs `diff` between the source lockfile and the
|
|
# npm-deps cache lockfile. fetchNpmDeps preserves whatever trailing
|
|
# newlines the lockfile has. The patchPhase normalizes to exactly one
|
|
# trailing newline so both sides always match.
|
|
#
|
|
# Usage:
|
|
# npm = hermesNpmLib.mkNpmPassthru { folder = "ui-tui"; attr = "tui"; pname = "hermes-tui"; };
|
|
# pkgs.buildNpmPackage (npm // { ... } # or:
|
|
# pkgs.buildNpmPackage ({ ... } // npm)
|
|
mkNpmPassthru =
|
|
{
|
|
folder, # repo-relative folder with package.json, e.g. "ui-tui"
|
|
attr, # flake package attr, e.g. "tui"
|
|
pname, # e.g. "hermes-tui"
|
|
nixFile ? "nix/${attr}.nix", # defaults to nix/<attr>.nix
|
|
}:
|
|
{
|
|
patchPhase = ''
|
|
runHook prePatch
|
|
# Normalize trailing newlines so source and npm-deps always match,
|
|
# regardless of what fetchNpmDeps preserves.
|
|
sed -i -z 's/\n*$/\n/' package-lock.json
|
|
|
|
# Make npmConfigHook's byte-for-byte diff newline-agnostic by
|
|
# replacing its hardcoded /nix/store/.../diff with a wrapper that
|
|
# normalizes trailing newlines on both sides before comparing.
|
|
mkdir -p "$TMPDIR/bin"
|
|
cat > "$TMPDIR/bin/diff" << DIFFWRAP
|
|
#!/bin/sh
|
|
f1=\$(mktemp) && sed -z 's/\n*$/\n/' "\$1" > "\$f1"
|
|
f2=\$(mktemp) && sed -z 's/\n*$/\n/' "\$2" > "\$f2"
|
|
${pkgs.diffutils}/bin/diff "\$f1" "\$f2" && rc=0 || rc=\$?
|
|
rm -f "\$f1" "\$f2"
|
|
exit \$rc
|
|
DIFFWRAP
|
|
chmod +x "$TMPDIR/bin/diff"
|
|
export PATH="$TMPDIR/bin:$PATH"
|
|
|
|
runHook postPatch
|
|
'';
|
|
|
|
nativeBuildInputs = [
|
|
(pkgs.writeShellScriptBin "update_${attr}_lockfile" ''
|
|
set -euox pipefail
|
|
|
|
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
|
|
cd "$REPO_ROOT/${folder}"
|
|
rm -rf node_modules/
|
|
npm cache clean --force
|
|
CI=true npm install
|
|
${pkgs.lib.getExe npm-lockfile-fix} ./package-lock.json
|
|
|
|
NIX_FILE="$REPO_ROOT/${nixFile}"
|
|
sed -i "s/hash = \"[^\"]*\";/hash = \"\";/" $NIX_FILE
|
|
NIX_OUTPUT=$(nix build .#${attr} 2>&1 || true)
|
|
NEW_HASH=$(echo "$NIX_OUTPUT" | grep 'got:' | awk '{print $2}')
|
|
echo got new hash $NEW_HASH
|
|
sed -i "s|hash = \"[^\"]*\";|hash = \"$NEW_HASH\";|" $NIX_FILE
|
|
nix build .#${attr}
|
|
echo "Updated npm hash in $NIX_FILE to $NEW_HASH"
|
|
'')
|
|
];
|
|
|
|
passthru = {
|
|
devShellHook = pkgs.writeShellScript "npm-dev-hook-${pname}" ''
|
|
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
|
|
_hermes_npm_stamp() {
|
|
sha256sum "${folder}/package.json" "${folder}/package-lock.json" \
|
|
2>/dev/null | sha256sum | awk '{print $1}'
|
|
}
|
|
STAMP=".nix-stamps/${pname}"
|
|
STAMP_VALUE="$(_hermes_npm_stamp)"
|
|
if [ ! -f "$STAMP" ] || [ "$(cat "$STAMP")" != "$STAMP_VALUE" ]; then
|
|
echo "${pname}: installing npm dependencies..."
|
|
( cd ${folder} && CI=true npm install --silent --no-fund --no-audit 2>/dev/null )
|
|
|
|
# Auto-update the nix hash so it stays in sync with the lockfile
|
|
echo "${pname}: prefetching npm deps..."
|
|
NIX_FILE="$REPO_ROOT/${nixFile}"
|
|
if NEW_HASH=$(${pkgs.lib.getExe pkgs.prefetch-npm-deps} "${folder}/package-lock.json" 2>/dev/null); then
|
|
sed -i "s|hash = \"sha256-[A-Za-z0-9+/=]+\"|hash = \"$NEW_HASH\";|" "$NIX_FILE"
|
|
echo "${pname}: updated hash to $NEW_HASH"
|
|
else
|
|
echo "${pname}: warning: prefetch failed, run 'nix run .#fix-lockfiles -- --apply' manually" >&2
|
|
fi
|
|
|
|
mkdir -p .nix-stamps
|
|
_hermes_npm_stamp > "$STAMP"
|
|
fi
|
|
unset -f _hermes_npm_stamp
|
|
'';
|
|
|
|
npmLockfile = {
|
|
inherit attr folder nixFile;
|
|
};
|
|
};
|
|
};
|
|
|
|
# Aggregate `fix-lockfiles` bin from a list of packages carrying
|
|
# passthru.npmLockfile = { attr; folder; nixFile; };
|
|
# Invocations:
|
|
# fix-lockfiles --check # exit 1 if any hash is stale
|
|
# fix-lockfiles --apply # rewrite stale hashes in place
|
|
# Writes machine-readable fields (stale, changed, report) to $GITHUB_OUTPUT
|
|
# when set, so CI workflows can post a sticky PR comment directly.
|
|
mkFixLockfiles =
|
|
{
|
|
packages, # list of packages with passthru.npmLockfile
|
|
}:
|
|
let
|
|
entries = map (p: p.passthru.npmLockfile) packages;
|
|
entryArgs = pkgs.lib.concatMapStringsSep " " (e: "\"${e.attr}:${e.folder}:${e.nixFile}\"") entries;
|
|
in
|
|
pkgs.writeShellScriptBin "fix-lockfiles" ''
|
|
set -uox pipefail
|
|
MODE="''${1:---check}"
|
|
case "$MODE" in
|
|
--check|--apply) ;;
|
|
-h|--help)
|
|
echo "usage: fix-lockfiles [--check|--apply]"
|
|
exit 0 ;;
|
|
*)
|
|
echo "usage: fix-lockfiles [--check|--apply]" >&2
|
|
exit 2 ;;
|
|
esac
|
|
|
|
ENTRIES=(${entryArgs})
|
|
|
|
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
|
cd "$REPO_ROOT"
|
|
|
|
# When running in GH Actions, emit Markdown links in the report pointing
|
|
# at the offending line of the nix file (and the lockfile) at the exact
|
|
# commit that was checked. LINK_SHA should be set by the workflow to the
|
|
# PR head SHA; falls back to GITHUB_SHA (which on pull_request is the
|
|
# test-merge commit, still browseable).
|
|
LINK_SERVER="''${GITHUB_SERVER_URL:-https://github.com}"
|
|
LINK_REPO="''${GITHUB_REPOSITORY:-}"
|
|
LINK_SHA="''${LINK_SHA:-''${GITHUB_SHA:-}}"
|
|
|
|
STALE=0
|
|
FIXED=0
|
|
REPORT=""
|
|
|
|
for entry in "''${ENTRIES[@]}"; do
|
|
IFS=":" read -r ATTR FOLDER NIX_FILE <<< "$entry"
|
|
echo "==> .#$ATTR ($FOLDER -> $NIX_FILE)"
|
|
OUTPUT=$(nix build ".#$ATTR.npmDeps" --no-link --rebuild --print-build-logs 2>&1)
|
|
STATUS=$?
|
|
if [ "$STATUS" -eq 0 ]; then
|
|
echo " ok"
|
|
continue
|
|
fi
|
|
|
|
NEW_HASH=$(echo "$OUTPUT" | awk '/got:/ {print $2; exit}')
|
|
if [ -z "$NEW_HASH" ]; then
|
|
echo " build failed with no hash mismatch:" >&2
|
|
echo "$OUTPUT" | tail -40 >&2
|
|
exit 1
|
|
fi
|
|
|
|
HASH_LINE=$(grep -n 'hash = "sha256-' "$NIX_FILE" | head -1 | cut -d: -f1)
|
|
OLD_HASH=$(grep -oE 'hash = "sha256-[^"]+"' "$NIX_FILE" | head -1 \
|
|
| sed -E 's/hash = "(.*)"/\1/')
|
|
LOCK_FILE="$FOLDER/package-lock.json"
|
|
echo " stale: $NIX_FILE:$HASH_LINE $OLD_HASH -> $NEW_HASH"
|
|
STALE=1
|
|
|
|
if [ -n "$LINK_REPO" ] && [ -n "$LINK_SHA" ]; then
|
|
NIX_URL="$LINK_SERVER/$LINK_REPO/blob/$LINK_SHA/$NIX_FILE#L$HASH_LINE"
|
|
LOCK_URL="$LINK_SERVER/$LINK_REPO/blob/$LINK_SHA/$LOCK_FILE"
|
|
REPORT+="- [\`$NIX_FILE:$HASH_LINE\`]($NIX_URL) (\`.#$ATTR\`): \`$OLD_HASH\` → \`$NEW_HASH\` — lockfile: [\`$LOCK_FILE\`]($LOCK_URL)"$'\n'
|
|
else
|
|
REPORT+="- \`$NIX_FILE:$HASH_LINE\` (\`.#$ATTR\`): \`$OLD_HASH\` → \`$NEW_HASH\`"$'\n'
|
|
fi
|
|
|
|
if [ "$MODE" = "--apply" ]; then
|
|
sed -i "s|hash = \"sha256-[^\"]*\";|hash = \"$NEW_HASH\";|" "$NIX_FILE"
|
|
nix build ".#$ATTR.npmDeps" --no-link --print-build-logs
|
|
FIXED=1
|
|
echo " fixed"
|
|
fi
|
|
done
|
|
|
|
if [ -n "''${GITHUB_OUTPUT:-}" ]; then
|
|
{
|
|
[ "$STALE" -eq 1 ] && echo "stale=true" || echo "stale=false"
|
|
[ "$FIXED" -eq 1 ] && echo "changed=true" || echo "changed=false"
|
|
if [ -n "$REPORT" ]; then
|
|
echo "report<<REPORT_EOF"
|
|
printf "%s" "$REPORT"
|
|
echo "REPORT_EOF"
|
|
fi
|
|
} >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
if [ "$STALE" -eq 1 ] && [ "$MODE" = "--check" ]; then
|
|
echo
|
|
echo "Stale lockfile hashes detected. Run:"
|
|
echo " nix run .#fix-lockfiles -- --apply"
|
|
exit 1
|
|
fi
|
|
|
|
exit 0
|
|
'';
|
|
}
|