fix(nix): check per-output binary-cache status in InputAddressedPathForOutput (#2921) - #2922
fix(nix): check per-output binary-cache status in InputAddressedPathForOutput (#2921)#2922mikeland73 wants to merge 1 commit into
Conversation
…orOutput InputAddressedPathForOutput returns the input-addressed store path for a single output, but it guarded on IsInBinaryCache(), which requires every one of the package's default outputs to be present in the binary cache. The flake template calls this per output, and only for outputs it has already determined are cached (via GetOutputsWithCache, which checks each output individually). For a multi-output package (e.g. curl, dnsutils) whose requested output is cached but some other default output is not, the template entered the fetchClosure block yet the stricter guard failed, raising "Package X cannot be fetched from binary cache store" at flake.nix.tmpl:52. Switch the guard to IsOutputInBinaryCache(output) so it concerns only the output being fetched, matching InstallableForOutput which already gates on the per-output check. Fixes #2921
There was a problem hiding this comment.
Pull request overview
Fixes a regression in internal/devpkg.Package.InputAddressedPathForOutput where multi-output packages could fail flake generation even when the requested output is available in a binary cache, due to the function incorrectly requiring all default outputs to be cached.
Changes:
- Update
InputAddressedPathForOutput(output)to guard usingIsOutputInBinaryCache(output)instead ofIsInBinaryCache(). - Improve the failure diagnostic by including the specific output name in the error message.
- Add an explanatory comment documenting the per-output vs all-outputs cache-check mismatch and linking it to issue #2921.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Anything to get movement here. We are blocked on upgrading devbox without this fix. |
| // Check that this specific output is in the binary cache, rather than | ||
| // requiring every one of the package's default outputs to be cached. | ||
| // | ||
| // The flake template calls this per-output, and only for outputs it has | ||
| // already determined are cached (via GetOutputsWithCache, which checks each | ||
| // output individually). A multi-output package can have the requested | ||
| // output cached while some other default output is not. Guarding with the | ||
| // stricter IsInBinaryCache (which requires all default outputs) would | ||
| // spuriously fail in that case with "cannot be fetched from binary cache | ||
| // store", even though the output being fetched here is present. This also | ||
| // matches InstallableForOutput, which already gates on IsOutputInBinaryCache. | ||
| // See issue #2921. |
There was a problem hiding this comment.
This makes sense, but I don't think this comment should be added here in the source code. Outside of this specific issue's bug fix, we don't need this comment's information for understanding the functionality of this function.
Summary
Fixes #2921.
Devbox 0.17.3 regressed so that
devbox shellenv(and any command that regenerates the environment) fails during flake generation with:for multi-output packages (e.g.
dnsutils,curl) when the requested output is available in the binary cache but not every one of the package's default outputs is.Root cause
The error is raised by
InputAddressedPathForOutput(internal/devpkg/package.go), called from the flake template atinternal/shellgen/tmpl/flake.nix.tmpl:52— exactly where the reported stack trace points.That template line only runs inside
{{ if $output.CacheURI }}, andCacheURIis set per output byGetOutputsWithCache→fetchNarInfoStatusOnce(name), which checks that single output.But
InputAddressedPathForOutputinternally guarded onIsInBinaryCache(), which is the all-default-outputs check (areExpectedOutputsInCacheOnce(useDefaultOutputs)requireslen(found) == len(defaultOutputs)).So for a package whose requested output is cached while some other default output is not, the template committed to emitting a
builtins.fetchClosurefor the cached output, yet the stricter internal guard reported the whole package as not-cached and returned the error. The per-output check (template) and the all-outputs check (guard) disagreed. This is the only code path that produces that error message at that template line.Fix
InputAddressedPathForOutputreturns the input-addressed path for a single output, so its precondition should concern only that output. Switch its guard fromIsInBinaryCache()toIsOutputInBinaryCache(output). This mirrorsInstallableForOutput, which already gates the very same call on the per-output check:The plural
InputAddressedPaths()is unchanged: it still guards onIsInBinaryCache()(it legitimately wants all default outputs) before iterating, so relaxing the singular guard cannot weaken it — each per-output check there is a subset of the all-outputs check the caller already passed.The error message now names the specific output for clearer diagnostics.
How was it tested?
go build ./...andgo vet ./internal/devpkg/— clean.go test ./internal/devpkg/— passes."... cannot be fetched from binary cache store"atflake.nix.tmpl:52, and that the fix restores agreement between the template's per-output caching decision and the guard.Note: the binary-cache resolution path gates on a Nix ≥ 2.17 install (
sysInfoIfExists→nix.AtLeast), so an end-to-end reproduction requires a Nix environment with a partially-populated cache (as in the issue). I was unable to run that scenario in the sandbox used here; reviewers with a Nix toolchain can confirm against the issue's repro steps.cc @sstarcher (issue reporter) — thanks for the detailed diagnosis and stack trace.
Community Contribution License
All community contributions in this pull request are licensed to the project
maintainers under the terms of the
Apache 2 License.
By creating this pull request, I represent that I have the right to license the
contributions to the project maintainers under the Apache 2 License as stated in
the
Community Contribution License.
🤖 Generated with Claude Code
https://claude.ai/code/session_017rMbKrge1aJW6mNbGuPGDS
Generated by Claude Code