Repository navigation
perf(nixprofile): read manifest.json instead of running nix profile list - #3030
Merged
Merged
Conversation
Implements item 10 of specs/perf-improvements.md. ProfileListItems ran `nix profile list --json` (~75ms) on every state recompute. For a version 3 manifest (nix >= 2.20), that command reads <profile>/manifest.json and prints it back out in the same format, so ProfileListItems now reads the file instead. - Only version 3 is read directly. Versions 1 and 2 keep elements in an array and newer nix derives names for them when reading, so those go to nix, as do unknown versions, unparseable files, and elements nix would reject (missing "active"). - Store paths are sorted and deduplicated, and originalUrl/attrPath are dropped when url is empty, matching what nix prints. - A missing manifest (no profile yet, or a link to a garbage collected generation) returns no items, like nix. A manifest.nix (nix-env profile) goes to nix. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
Collaborator
Author
|
@claude ptal |
|
Claude finished @mikeland73's task in 26s —— View job Review of #3030I found no blocking issues. I read What looks right
Nits (optional)
Overall this looks good to merge, assuming CI is green. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements item 10 of
specs/perf-improvements.md.nixprofile.ProfileListItemsrannix profile list --jsonon every staterecompute, which took ~75ms. In nix >= 2.20, that command parses
<profile>/manifest.jsonand prints it back out (ProfileManifest::toJSONin
src/nix/profile.cc). The file nix writes uses the sametoJSON, so fora version 3 manifest the two are the same.
ProfileListItemsnow reads thefile and only runs the command when it can't be sure.
2.20 derives a name for each one when it reads them (
getNameFromURL,plus
-1,-2suffixes for duplicates), so the command returns nameditems while the file only has indexes. Rather than copy that logic, those
versions go to the command. nix rewrites them as version 3 the next time
it changes the profile.
into the expected types, elements missing
active(nix fails on thosetoday), and a read error other than "not found".
keeps them in a sorted set), a missing
storePathsbecomes an empty list,and
originalUrl/attrPathare ignored whenurlis empty (nix drops thesource then).
manifest.jsonand nomanifest.nixis empty. That covers a profile that doesn't exist yet anda
default -> default-N-linkthat points at a garbage collected storepath. A
manifest.nix(nix-env profile) goes to the command. The profilesymlinks are followed by the normal file read, the same way nix reads
profile / "manifest.json".the element-to-item conversion.
The only production caller is
syncNixProfileFromFlake, which only usesthe store paths.
ProfileListNameOrIndexhas no callers.Testing
element, two-output element with unsorted store paths, empty url,
duplicate store paths), versions 1, 2, 4, missing version and malformed
files (all fall back), the profile -> profile-1-link -> generation layout,
missing / dangling / empty profiles (no command run), and fallback for v1,
v2, v4, bad JSON, manifest.nix and an unreadable manifest (stubbed
command).
TestManifestMatchesNixProfileListwrites the fixtures to temp profilesand checks that reading the file gives the same items as
profileListItemsFromCommand. It needs nix >= 2.20 and is skippedotherwise. nix only parses the store paths, so they don't need to exist.
committed) that ran both the file and the command paths and compared the
items: 72 profiles on my machine. 70 matched: 10 devbox project and
global profiles with 1–17 packages; a profile made with
nix profile addof
nixpkgs#hello,nixpkgs#jq^bin,manand a store path, so it hasflake URLs and multiple outputs; 57 devbox profiles whose generation was
garbage collected;
~/.nix-profile, which points at a missingdirectory; and a path that doesn't exist. The last three kinds give no
items either way. The other 2 were hand-made v1 and v2 manifests, which
fell back to the command as intended. I also diffed
jq -Sofmanifest.jsonagainst
nix profile list --jsonfor all of the v3 profiles, and theywere byte-for-byte the same.
devbox.json, removing a package and runningdevbox installremoved itfrom the profile, adding it back installed it, and recomputes with no
changes left the profile generation alone.
go test ./internal/nix/... ./internal/devbox/passes.go tool golangci-lint run ./internal/nix/nixprofile/... ./internal/devbox/...reports 0 issues.
ProfileListItems(benchmark, 30 runs)ProfileListItemsfunction timer duringdevbox shellenv --recompute(10 runs)Not handled
originalUrl: "nixpkgs"prints asflake:nixpkgs). Files nix writesalready hold the normalized form, so this only matters for a manifest
written by a nix whose normalization differs from the one installed now.
Even then only
unlockedReference/lockedReferencecould differ, and theonly caller doesn't use them.
the command fails today. Now the file is still read. Later
nix profilecalls would fail the same way as before.
🤖 Generated with Claude Code