conandeps: always show the binary's source; cache-only binaries are not available

The tool's question is "does the remote have this binary?", so local cache
state must not mask the answer. Conan reports Cache for a locally present
binary without consulting the remote; for those nodes we now run a package
search on the remote(s) and only count the binary as available if the same
package_id is found there. Binaries that exist only in the local cache are
rendered as "not available", listed under missing binaries with a note, and
make the exit status 1.

Every available cell now names the remote that has the binary instead of a
bare "ok". Adds a test that builds a package locally without uploading it
and checks it is reported as cache-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYNdaGDrpaqDyT8QtrTQbM
This commit is contained in:
Ole-Morten Duesund 2026-08-25 15:14:54 +02:00
commit 1724cb2637
4 changed files with 135 additions and 33 deletions

View file

@ -228,11 +228,40 @@ def test_missing_binaries(report):
for dep in report.deps.values():
for bt, (status, pid, remote) in dep.binaries.items():
if dep.ref != "libbar/1.1":
assert status == "Download", (dep.ref, bt, status)
assert status == "Remote", (dep.ref, bt, status)
assert remote == "test"
assert pid
def test_cache_only_is_not_available(conan_env):
"""A binary that exists only in the local cache must not count as available.
Build libbar/1.1 Debug locally without uploading it, and install the
Release one so it is cached too. Release must still be attributed to the
remote; Debug must be reported as cache-only (a problem).
"""
env = dict(os.environ)
d = Path(conan_env["home"]) / "libbar-local"
d.mkdir()
(d / "conanfile.py").write_text(RECIPE.format(name="libbar", version="1.1", requires=""))
_conan(env, "create", str(d), "-s", "build_type=Debug")
_conan(env, "install", "libbar/1.1@", "-s", "build_type=Release", "-r", "test")
report = conandeps.build_report(
conan_env["conanfile"], "test", [], [], [], list(BUILD_TYPES), update=False, quiet=True
)
libbar = report.deps["libbar#host"]
assert libbar.binaries["Release"][0] == "Remote"
assert libbar.binaries["Release"][2] == "test"
assert libbar.binaries["Debug"][0] == "CacheOnly"
assert libbar.binaries["RelWithDebInfo"][0] == "Missing"
assert libbar.missing() == ["Debug", "RelWithDebInfo"]
text = conandeps.render_text(report)
assert "not available" in text and "in local cache only" in text
_conan(env, "remove", "libbar/1.1@", "-f") # leave the cache as other tests expect
def test_overrides(report):
ov = {(o.package, o.old, o.new, o.by, o.explicit) for o in report.overrides}
assert ov == {
@ -244,6 +273,8 @@ def test_overrides(report):
def test_text_and_html_render(report, tmp_path):
text = conandeps.render_text(report)
assert "libbar/1.1" in text and "MISSING" in text
table = text.split("MISSING BINARIES")[0]
assert table.count("test") >= 4 * 3 - 2 # every available cell names the remote
assert "overrides libbar/1.0 (explicit" in text and "explicit override=True" in text
assert "`-- " in text or "|-- " in text # tree drawn
page = conandeps.render_html(report)