Files
hermes-agent/nix/pm-packages.nix
ethernet e34214118f nix: unpack only the archive artifacts of a pm pin
pm/lock.json lists provenance sidecars (checksums.txt, .sig/.pem/.asc)
next to the tirith and iron-proxy archives; pm verifies them at install
time. nix/pm-packages.nix fed every artifact to srcs, and stdenv's
unpackPhase has no unpacker for a .txt, so `nix build .#pm-tirith`
failed with "do not know how to unpack source archive". `nix flake
check` only evaluated the pm derivations, which is why CI stayed green.

srcs now keeps the suffixes pm/store.py::extract unpacks; the
fixed-output hash already pins the archive. A new check builds the two
sidecar-bearing pins so the regression cannot hide behind evaluation.
2026-09-21 18:44:09 -04:00

78 lines
2.6 KiB
Nix

{
lib,
stdenv,
fetchurl,
unzip,
}:
# The thin translation layer: pm/lock.json -> one fetched, unpacked
# derivation per package for this nix system. Nothing here knows versions,
# urls, or hashes — the lockfile is the complete machine interface, the
# same one `pm install` and `pm bundle` consume.
#
# Consumers pick what they need: (callPackage ./pm-packages.nix { }).ripgrep
# Packages with no artifact for this system are simply absent.
let
lock = builtins.fromJSON (builtins.readFile ../pm/lock.json);
mirror = builtins.fromJSON (builtins.readFile ../pm/artifact-mirror.json);
target =
let
arch = if stdenv.hostPlatform.isAarch64 then "arm64" else "x64";
os =
if stdenv.hostPlatform.isDarwin then "darwin"
else if stdenv.hostPlatform.isLinux then "linux"
else "win32";
in
"${os}-${arch}";
# A target pins one artifact or a list of them (a runtime split across
# archives that must land in one directory). Normalize to a list.
artifactsFor =
pin:
let
found = pin.artifacts.${target} or pin.artifacts.any or null;
in
if found == null then null else if builtins.isList found then found else [ found ];
# The archive suffixes pm/store.py::extract unpacks. A pin may also list
# provenance sidecars (checksums.txt, .sig/.pem/.asc) that pm verifies at
# install time; the fixed-output hash already pins the archive here, and
# stdenv's unpackPhase has no unpacker for them, so they are left out.
isArchive = artifact:
lib.any (suffix: lib.hasSuffix suffix artifact.url) [ ".tar.gz" ".tgz" ".tar.xz" ".txz" ".tar.bz2" ".zip" ];
derive = name: pin: artifacts:
stdenv.mkDerivation {
pname = name;
version = pin.version;
srcs = map (artifact: fetchurl {
urls = [ artifact.url "${mirror.origin}/${mirror.prefix}${artifact.sha256}" ];
sha256 = artifact.sha256;
}) (builtins.filter isArchive artifacts);
# pm's store publishes the unpacked tree; mirror that shape. Several
# archives unpack over one another into the same root, exactly as
# pm merges them into one store entry.
sourceRoot = ".";
nativeBuildInputs =
lib.optional (lib.any (a: lib.hasSuffix ".zip" a.url) artifacts) unzip;
dontBuild = true;
dontConfigure = true;
installPhase = ''
mkdir -p $out
cp -r . $out/
'';
};
in
lib.filterAttrs (_: v: v != null) (
lib.mapAttrs (
name: pin:
let
artifacts = artifactsFor pin;
in
if artifacts == null then null else derive name pin artifacts
) lock.packages
)