Repository: conan-io/conan
Pull request #18544 (2025-06-26 12:37:16)

Fix: Fix cache check-intregrity output
Changelog: Fix: Improve `conan cache check-integiry` output.
Docs: missing

closes https://github.com/conan-io/conan/issues/18528



Patch:
diff --git a/conan/internal/cache/integrity_check.py b/conan/internal/cache/integrity_check.py
index b1148e612..43b526d11 100644
--- a/conan/internal/cache/integrity_check.py
+++ b/conan/internal/cache/integrity_check.py
@@ -31,11 +31,11 @@ class IntegrityChecker:
 
     def _recipe_corrupted(self, ref: RecipeReference):
         layout = self._cache.recipe_layout(ref)
-        output = ConanOutput()
+        output = ConanOutput(scope=f"{ref.repr_notime()}")
         try:
             read_manifest, expected_manifest = layout.recipe_manifests()
         except FileNotFoundError:
-            output.error(f"{ref.repr_notime()}: Manifest missing", error_type="exception")
+            output.error("Manifest missing", error_type="exception")
             return True
         # Filter exports_sources from read manifest if there are no exports_sources locally
         # This happens when recipe is downloaded without sources (not built from source)
@@ -45,28 +45,28 @@ class IntegrityChecker:
                                        if not k.startswith("export_source")}
 
         if read_manifest != expected_manifest:
-            output.error(f"{ref}: Manifest mismatch", error_type="exception")
-            output.error(f"Folder: {layout.export()}", error_type="exception")
+            output_lines = ["", "Manifest mismatch", f"    Folder: {layout.package()}"]
             diff = read_manifest.difference(expected_manifest)
             for fname, (h1, h2) in diff.items():
-                output.error(f"    '{fname}' (manifest: {h1}, file: {h2})", error_type="exception")
+                output_lines.append(f"        {fname} (manifest: {h1}, file: {h2})")
+            output.error("\n".join(output_lines), error_type="exception")
             return True
-        output.info(f"{ref}: Integrity checked: ok")
+        output.info("Integrity check: ok")
 
     def _package_corrupted(self, ref: PkgReference):
         layout = self._cache.pkg_layout(ref)
-        output = ConanOutput()
+        output = ConanOutput(scope=f"{ref.repr_notime()}")
         try:
             read_manifest, expected_manifest = layout.package_manifests()
         except FileNotFoundError:
-            output.error(f"{ref.repr_notime()}: Manifest missing", error_type="exception")
+            output.error("Manifest missing", error_type="exception")
             return True
 
         if read_manifest != expected_manifest:
-            output.error(f"{ref}: Manifest mismatch", error_type="exception")
-            output.error(f"Folder: {layout.package()}", error_type="exception")
+            output_lines = ["", "Manifest mismatch", f"    Folder: {layout.package()}"]
             diff = read_manifest.difference(expected_manifest)
             for fname, (h1, h2) in diff.items():
-                output.error(f"    '{fname}' (manifest: {h1}, file: {h2})", error_type="exception")
+                output_lines.append(f"        {fname} (manifest: {h1}, file: {h2})")
+            output.error("\n".join(output_lines), error_type="exception")
             return True
-        output.info(f"{ref}: Integrity checked: ok")
+        output.info("Integrity check: ok")


Test patch:
diff --git a/test/integration/command/cache/test_cache_integrity.py b/test/integration/command/cache/test_cache_integrity.py
index cbbf69a61..b40a2f9a6 100644
--- a/test/integration/command/cache/test_cache_integrity.py
+++ b/test/integration/command/cache/test_cache_integrity.py
@@ -30,20 +30,24 @@ def test_cache_integrity(use_pkglist):
     arg = "--list=pkglist.json" if use_pkglist else "*"
 
     t.run(f"cache check-integrity {arg}", assert_error=True)
-    assert "pkg1/1.0: Integrity checked: ok" in t.out
-    assert "pkg1/1.0:da39a3ee5e6b4b0d3255bfef95601890afd80709: Integrity checked: ok" in t.out
-    assert "ERROR: pkg2/2.0:da39a3ee5e6b4b0d3255bfef95601890afd80709: Manifest mismatch" in t.out
-    assert "ERROR: pkg3/3.0:da39a3ee5e6b4b0d3255bfef95601890afd80709: Manifest mismatch" in t.out
-    assert "ERROR: pkg4/4.0:da39a3ee5e6b4b0d3255bfef95601890afd80709: Manifest mismatch" in t.out
+    assert "pkg1/1.0#4d670581ccb765839f2239cc8dff8fbd: Integrity check: ok" in t.out
+    assert "pkg1/1.0#4d670581ccb765839f2239cc8dff8fbd:da39a3ee5e6b4b0d3255bfef95601890afd80709" \
+           "#0ba8627bd47edc3a501e8f0eb9a79e5e: Integrity check: ok" in t.out
+    assert "pkg2/2.0#4d670581ccb765839f2239cc8dff8fbd:da39a3ee5e6b4b0d3255bfef95601890afd80709" \
+           "#0ba8627bd47edc3a501e8f0eb9a79e5e: ERROR: \nManifest mismatch" in t.out
+    assert "pkg3/3.0#4d670581ccb765839f2239cc8dff8fbd:da39a3ee5e6b4b0d3255bfef95601890afd80709" \
+           "#0ba8627bd47edc3a501e8f0eb9a79e5e: ERROR: \nManifest mismatch" in t.out
+    assert "pkg4/4.0#4d670581ccb765839f2239cc8dff8fbd:da39a3ee5e6b4b0d3255bfef95601890afd80709" \
+           "#0ba8627bd47edc3a501e8f0eb9a79e5e: ERROR: \nManifest mismatch" in t.out
 
     t.run("remove pkg2/2.0:da39a3ee5e6b4b0d3255bfef95601890afd80709 -c")
     t.run("remove pkg3/3.0:da39a3ee5e6b4b0d3255bfef95601890afd80709 -c")
     t.run("remove pkg4/4.0:da39a3ee5e6b4b0d3255bfef95601890afd80709 -c")
     t.run("cache check-integrity *")
-    assert "pkg1/1.0: Integrity checked: ok" in t.out
-    assert "pkg2/2.0: Integrity checked: ok" in t.out
-    assert "pkg3/3.0: Integrity checked: ok" in t.out
-    assert "pkg4/4.0: Integrity checked: ok" in t.out
+    assert "pkg1/1.0#4d670581ccb765839f2239cc8dff8fbd: Integrity check: ok" in t.out
+    assert "pkg2/2.0#4d670581ccb765839f2239cc8dff8fbd: Integrity check: ok" in t.out
+    assert "pkg3/3.0#4d670581ccb765839f2239cc8dff8fbd: Integrity check: ok" in t.out
+    assert "pkg4/4.0#4d670581ccb765839f2239cc8dff8fbd: Integrity check: ok" in t.out
 
 
 def test_cache_integrity_missing_recipe_manifest():
@@ -57,15 +61,16 @@ def test_cache_integrity_missing_recipe_manifest():
     t.run("create . --name pkg3 --version=3.0")
 
     t.run("cache check-integrity *", assert_error=True)
-    assert "pkg1/1.0: Integrity checked: ok" in t.out
-    assert "ERROR: pkg2/2.0#4d670581ccb765839f2239cc8dff8fbd: Manifest missing" in t.out
-    assert "pkg3/3.0: Integrity checked: ok" in t.out
+    assert "pkg1/1.0#4d670581ccb765839f2239cc8dff8fbd: Integrity check: ok" in t.out
+    assert "pkg2/2.0#4d670581ccb765839f2239cc8dff8fbd: ERROR: Manifest missing" in t.out
+    assert "pkg3/3.0#4d670581ccb765839f2239cc8dff8fbd: Integrity check: ok" in t.out
     assert "ERROR: There are corrupted artifacts, check the error logs" in t.out
 
     t.run("remove pkg2* -c")
     t.run("cache check-integrity *")
-    assert "pkg1/1.0:da39a3ee5e6b4b0d3255bfef95601890afd80709: Integrity checked: ok" in t.out
-    assert "pkg3/3.0: Integrity checked: ok" in t.out
+    assert "pkg1/1.0#4d670581ccb765839f2239cc8dff8fbd:da39a3ee5e6b4b0d3255bfef95601890afd80709" \
+           "#0ba8627bd47edc3a501e8f0eb9a79e5e: Integrity check: ok" in t.out
+    assert "pkg3/3.0#4d670581ccb765839f2239cc8dff8fbd: Integrity check: ok" in t.out
     assert "Integrity check: ok" in t.out
 
 
@@ -80,17 +85,17 @@ def test_cache_integrity_missing_package_manifest():
     t.run("create . --name pkg3 --version=3.0")
 
     t.run("cache check-integrity *", assert_error=True)
-    assert "pkg1/1.0: Integrity checked: ok" in t.out
-    assert "ERROR: pkg2/2.0#4d670581ccb765839f2239cc8dff8fbd" \
-           ":da39a3ee5e6b4b0d3255bfef95601890afd80709" \
-           "#0ba8627bd47edc3a501e8f0eb9a79e5e: Manifest missing" in t.out
-    assert "pkg3/3.0: Integrity checked: ok" in t.out
+    assert "pkg1/1.0#4d670581ccb765839f2239cc8dff8fbd: Integrity check: ok" in t.out
+    assert "pkg2/2.0#4d670581ccb765839f2239cc8dff8fbd:da39a3ee5e6b4b0d3255bfef95601890afd80709" \
+           "#0ba8627bd47edc3a501e8f0eb9a79e5e: ERROR: Manifest missing" in t.out
+    assert "pkg3/3.0#4d670581ccb765839f2239cc8dff8fbd: Integrity check: ok" in t.out
     assert "ERROR: There are corrupted artifacts, check the error logs" in t.out
 
     t.run("remove pkg2* -c")
     t.run("cache check-integrity *")
-    assert "pkg1/1.0:da39a3ee5e6b4b0d3255bfef95601890afd80709: Integrity checked: ok" in t.out
-    assert "pkg3/3.0: Integrity checked: ok" in t.out
+    assert "pkg1/1.0#4d670581ccb765839f2239cc8dff8fbd:da39a3ee5e6b4b0d3255bfef95601890afd80709" \
+           "#0ba8627bd47edc3a501e8f0eb9a79e5e: Integrity check: ok" in t.out
+    assert "pkg3/3.0#4d670581ccb765839f2239cc8dff8fbd: Integrity check: ok" in t.out
     assert "Integrity check: ok" in t.out
 
 
@@ -104,12 +109,26 @@ def test_cache_integrity_missing_package_conaninfo():
     os.remove(conaninfo)
 
     t.run("cache check-integrity *", assert_error=True)
-    assert "pkg1/1.0: Integrity checked: ok" in t.out
-    assert "ERROR: pkg2/2.0:da39a3ee5e6b4b0d3255bfef95601890afd80709: Manifest mismatch" in t.out
+    assert "pkg1/1.0#4d670581ccb765839f2239cc8dff8fbd: Integrity check: ok" in t.out
+    assert "pkg2/2.0#4d670581ccb765839f2239cc8dff8fbd:da39a3ee5e6b4b0d3255bfef95601890afd80709" \
+           "#0ba8627bd47edc3a501e8f0eb9a79e5e: ERROR: \nManifest mismatch" in t.out
 
     t.run("remove pkg2* -c")
     t.run("cache check-integrity *")
-    assert "pkg1/1.0:da39a3ee5e6b4b0d3255bfef95601890afd80709: Integrity checked: ok" in t.out
+    assert "pkg1/1.0#4d670581ccb765839f2239cc8dff8fbd:da39a3ee5e6b4b0d3255bfef95601890afd80709" \
+           "#0ba8627bd47edc3a501e8f0eb9a79e5e: Integrity check: ok" in t.out
+
+
+def test_cache_integrity_missing_package_file():
+    t = TestClient()
+    t.save({"conanfile.py": GenConanfile().with_package_file("myfile", "mycontents!")})
+    t.run("create . --name pkg --version 1.0")
+    layout = t.created_layout()
+    os.remove(os.path.join(layout.package(), "myfile"))
+
+    t.run("cache check-integrity *", assert_error=True)
+    assert "pkg/1.0#2f2609c8e5c87bf836c3fdaa6096b55d:da39a3ee5e6b4b0d3255bfef95601890afd80709" \
+           "#d950d0cd76f6bba62c8add9c68d1aeb3: ERROR: \nManifest mismatch" in t.out
 
 
 def test_cache_integrity_export_sources():
@@ -119,7 +138,7 @@ def test_cache_integrity_export_sources():
             "src/mysource.cpp": ""})
     t.run("create .")
     t.run("cache check-integrity *")
-    assert "pkg/0.1: Integrity checked: ok" in t.out
+    assert "pkg/0.1#ae07161b81ab07f7f1f746391668df0e: Integrity check: ok" in t.out
 
     # If we download, integrity should be ok
     # (it failed before, because the manifest is not complete)
@@ -127,4 +146,4 @@ def test_cache_integrity_export_sources():
     t.run("remove * -c")
     t.run("install --requires=pkg/0.1")
     t.run("cache check-integrity *")
-    assert "pkg/0.1: Integrity checked: ok" in t.out
+    assert "pkg/0.1#ae07161b81ab07f7f1f746391668df0e: Integrity check: ok" in t.out
diff --git a/test/integration/command/upload/upload_test.py b/test/integration/command/upload/upload_test.py
index 45e64ebe3..e8f6adcb0 100644
--- a/test/integration/command/upload/upload_test.py
+++ b/test/integration/command/upload/upload_test.py
@@ -177,8 +177,13 @@ class UploadTest(unittest.TestCase):
         save(os.path.join(package_folder, "added.txt"), "")
         os.remove(os.path.join(package_folder, "include/hello.h"))
         client.run("upload hello0/1.2.1@frodo/stable --check -r default", assert_error=True)
-        self.assertIn("ERROR:     'include/hello.h'", client.out)
-        self.assertIn("ERROR:     'added.txt'", client.out)
+        self.assertIn("hello0/1.2.1@frodo/stable#3afd661184b94bdac7fb2057e7bd9baa"
+                      ":da39a3ee5e6b4b0d3255bfef95601890afd80709"
+                      "#e70e86439dec07a0d5d3414648b0b16c: ERROR", client.out)
+        self.assertIn("include/hello.h (manifest: d41d8cd98f00b204e9800998ecf8427e, file: None)",
+                      client.out)
+        self.assertIn("added.txt (manifest: None, file: d41d8cd98f00b204e9800998ecf8427e)",
+                      client.out)
         self.assertIn("ERROR: There are corrupted artifacts, check the error logs", client.out)
 
     @pytest.mark.artifactory_ready
diff --git a/test/integration/conanfile/test_finalize_method.py b/test/integration/conanfile/test_finalize_method.py
index fe3f0363f..c18d79b34 100644
--- a/test/integration/conanfile/test_finalize_method.py
+++ b/test/integration/conanfile/test_finalize_method.py
@@ -364,5 +364,6 @@ class TestRemoteFlows:
         client.run("create dep")
         dep_layout = client.created_layout()
         client.run("upload * -r=default -c --check")
-        assert f"dep/1.0:{dep_layout.reference.package_id}: Integrity checked: ok" in client.out
+        assert f"dep/1.0#{dep_layout.reference.ref.revision}:{dep_layout.reference.package_id}" \
+               f"#{dep_layout.reference.revision}: Integrity check: ok" in client.out
         assert "There are corrupted artifacts" not in client.out