This is an automated email from the ASF dual-hosted git repository.

sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git


The following commit(s) were added to refs/heads/main by this push:
     new bc549f4  Use structured data in the RAT check task
bc549f4 is described below

commit bc549f4197dc0d7dc31d9021c10ff08603f3c3f0
Author: Sean B. Palmer <[email protected]>
AuthorDate: Mon Jan 12 19:39:40 2026 +0000

    Use structured data in the RAT check task
---
 atr/models/checkdata.py       |  42 +++++
 atr/tasks/checks/rat.py       | 358 +++++++++++++-----------------------------
 tests/unit/test_checks_rat.py |   4 +-
 3 files changed, 155 insertions(+), 249 deletions(-)

diff --git a/atr/models/checkdata.py b/atr/models/checkdata.py
new file mode 100644
index 0000000..c076987
--- /dev/null
+++ b/atr/models/checkdata.py
@@ -0,0 +1,42 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from . import schema
+
+
+class RatFileEntry(schema.Lax):
+    """Entry for a file with license issues."""
+
+    name: str = schema.default("")
+    license: str = schema.default("")
+
+
+class Rat(schema.Lax):
+    """Data from a RAT license check, stored in CheckResult.data."""
+
+    valid: bool = schema.default(False)
+    message: str = schema.default("")
+    total_files: int = schema.default(0)
+    approved_licenses: int = schema.default(0)
+    unapproved_licenses: int = schema.default(0)
+    unknown_licenses: int = schema.default(0)
+    errors: list[str] = schema.factory(list)
+    excludes_source: str = schema.default("unknown")
+    extended_std_applied: bool = schema.default(False)
+    warning: str | None = schema.default(None)
+    unapproved_files: list[RatFileEntry] = schema.factory(list)
+    unknown_license_files: list[RatFileEntry] = schema.factory(list)
diff --git a/atr/tasks/checks/rat.py b/atr/tasks/checks/rat.py
index bbe4bce..88772d8 100644
--- a/atr/tasks/checks/rat.py
+++ b/atr/tasks/checks/rat.py
@@ -21,12 +21,13 @@ import pathlib
 import subprocess
 import tempfile
 import xml.etree.ElementTree as ElementTree
-from typing import Any, Final
+from typing import Final
 
 import atr.archives as archives
 import atr.config as config
 import atr.constants as constants
 import atr.log as log
+import atr.models.checkdata as checkdata
 import atr.models.results as results
 import atr.models.sql as sql
 import atr.tasks.checks as checks
@@ -153,7 +154,7 @@ async def _check_core(
     artifact_abs_path: pathlib.Path,
     policy_excludes: list[str],
 ) -> None:
-    result_data = await asyncio.to_thread(
+    result = await asyncio.to_thread(
         _check_core_logic,
         artifact_path=str(artifact_abs_path),
         policy_excludes=policy_excludes,
@@ -162,39 +163,22 @@ async def _check_core(
         chunk_size=args.extra_args.get("chunk_size", 
_CONFIG.EXTRACT_CHUNK_SIZE),
     )
 
-    # This must come before the overall check result
-    # Otherwise the overall check result will contain the unknown license files
-    unknown_license_files = result_data.get("unknown_license_files", [])
-    if unknown_license_files:
-        for unknown_license_file in unknown_license_files:
-            await recorder.failure(
-                "Unknown license",
-                None,
-                member_rel_path=unknown_license_file["name"],
-            )
-    del result_data["unknown_license_files"]
-
-    unapproved_files = result_data.get("unapproved_files", [])
-    if unapproved_files:
-        for unapproved_file in unapproved_files:
-            await recorder.failure(
-                "Unapproved license",
-                {"license": unapproved_file["license"]},
-                member_rel_path=unapproved_file["name"],
-            )
-    del result_data["unapproved_files"]
-
-    if result_data.get("warning"):
-        await recorder.warning(result_data["warning"], result_data)
-    elif result_data.get("error"):
-        # Handle errors from within the core logic
-        await recorder.failure(result_data["message"], result_data)
-    elif not result_data["valid"]:
-        # Handle RAT validation failures
-        await recorder.failure(result_data["message"], result_data)
+    # Record individual file failures before the overall result
+    for file in result.unknown_license_files:
+        await recorder.failure("Unknown license", None, 
member_rel_path=file.name)
+
+    for file in result.unapproved_files:
+        await recorder.failure("Unapproved license", {"license": 
file.license}, member_rel_path=file.name)
+
+    # Convert to dict for storage, excluding the file lists, which are already 
recorded
+    result_data = result.model_dump(exclude={"unapproved_files", 
"unknown_license_files"})
+
+    if result.warning:
+        await recorder.warning(result.warning, result_data)
+    elif (not result.valid) or result.errors:
+        await recorder.failure(result.message, result_data)
     else:
-        # Handle success
-        await recorder.success(result_data["message"], result_data)
+        await recorder.success(result.message, result_data)
 
 
 def _check_core_logic(  # noqa: C901
@@ -203,7 +187,7 @@ def _check_core_logic(  # noqa: C901
     rat_jar_path: str = _CONFIG.APACHE_RAT_JAR_PATH,
     max_extract_size: int = _CONFIG.MAX_EXTRACT_SIZE,
     chunk_size: int = _CONFIG.EXTRACT_CHUNK_SIZE,
-) -> dict[str, Any]:
+) -> checkdata.Rat:
     """Verify license headers using Apache RAT."""
     log.info(f"Verifying licenses with Apache RAT for {artifact_path}")
     log.info(f"PATH environment variable: {os.environ.get('PATH', 'PATH not 
found')}")
@@ -223,27 +207,6 @@ def _check_core_logic(  # noqa: C901
         with tempfile.TemporaryDirectory(prefix="rat_verify_") as temp_dir:
             log.info(f"Created temporary directory: {temp_dir}")
 
-            # # Find and validate the root directory
-            # try:
-            #     root_dir = targz.root_directory(artifact_path)
-            # except targz.RootDirectoryError as e:
-            #     error_msg = str(e)
-            #     log.error(f"Archive root directory issue: {error_msg}")
-            #     return {
-            #         "valid": False,
-            #         "message": "No root directory found",
-            #         "total_files": 0,
-            #         "approved_licenses": 0,
-            #         "unapproved_licenses": 0,
-            #         "unknown_licenses": 0,
-            #         "unapproved_files": [],
-            #         "unknown_license_files": [],
-            #         "warning": error_msg or "No root directory found",
-            #         "errors": [],
-            #     }
-
-            # extract_dir = os.path.join(temp_dir, root_dir)
-
             # Extract the archive to the temporary directory
             log.info(f"Extracting {artifact_path} to {temp_dir}")
             extracted_size, exclude_file_paths = archives.extract(
@@ -259,18 +222,10 @@ def _check_core_logic(  # noqa: C901
             # Validate that we found at most one exclusion file
             if len(exclude_file_paths) > 1:
                 log.error(f"Multiple {_RAT_EXCLUDES_FILENAME} files found: 
{exclude_file_paths}")
-                return {
-                    "valid": False,
-                    "message": f"Multiple {_RAT_EXCLUDES_FILENAME} files not 
allowed (found {len(exclude_file_paths)})",
-                    "total_files": 0,
-                    "approved_licenses": 0,
-                    "unapproved_licenses": 0,
-                    "unknown_licenses": 0,
-                    "unapproved_files": [],
-                    "unknown_license_files": [],
-                    "errors": [f"Found {len(exclude_file_paths)} 
{_RAT_EXCLUDES_FILENAME} files"],
-                    "excludes_source": "unknown",
-                }
+                return checkdata.Rat(
+                    message=f"Multiple {_RAT_EXCLUDES_FILENAME} files not 
allowed (found {len(exclude_file_paths)})",
+                    errors=[f"Found {len(exclude_file_paths)} 
{_RAT_EXCLUDES_FILENAME} files"],
+                )
 
             # Narrow to single path after validation
             archive_excludes_path: str | None = exclude_file_paths[0] if 
exclude_file_paths else None
@@ -305,36 +260,22 @@ def _check_core_logic(  # noqa: C901
                 scan_root_is_inside = (abs_scan_root == abs_temp_dir) or 
abs_scan_root.startswith(abs_temp_dir + os.sep)
                 if not scan_root_is_inside:
                     log.error(f"Scan root {scan_root} is outside temp_dir 
{temp_dir}")
-                    return {
-                        "valid": False,
-                        "message": "Invalid archive structure: exclusion file 
path escapes extraction directory",
-                        "total_files": 0,
-                        "approved_licenses": 0,
-                        "unapproved_licenses": 0,
-                        "unknown_licenses": 0,
-                        "unapproved_files": [],
-                        "unknown_license_files": [],
-                        "errors": ["Exclusion file path escapes extraction 
directory"],
-                        "excludes_source": excludes_source,
-                    }
+                    return checkdata.Rat(
+                        message="Invalid archive structure: exclusion file 
path escapes extraction directory",
+                        errors=["Exclusion file path escapes extraction 
directory"],
+                        excludes_source=excludes_source,
+                    )
 
                 log.info(f"Using {_RAT_EXCLUDES_FILENAME} directory as scan 
root: {scan_root}")
 
                 untracked_count = _count_files_outside_directory(temp_dir, 
scan_root)
                 if untracked_count > 0:
                     log.error(f"Found {untracked_count} file(s) outside 
{_RAT_EXCLUDES_FILENAME} directory")
-                    return {
-                        "valid": False,
-                        "message": f"Files exist outside 
{_RAT_EXCLUDES_FILENAME} directory ({untracked_count} found)",
-                        "total_files": 0,
-                        "approved_licenses": 0,
-                        "unapproved_licenses": 0,
-                        "unknown_licenses": 0,
-                        "unapproved_files": [],
-                        "unknown_license_files": [],
-                        "errors": [f"{untracked_count} file(s) outside 
{_RAT_EXCLUDES_FILENAME} directory"],
-                        "excludes_source": excludes_source,
-                    }
+                    return checkdata.Rat(
+                        message=f"Files exist outside {_RAT_EXCLUDES_FILENAME} 
directory ({untracked_count} found)",
+                        errors=[f"{untracked_count} file(s) outside 
{_RAT_EXCLUDES_FILENAME} directory"],
+                        excludes_source=excludes_source,
+                    )
             else:
                 scan_root = temp_dir
                 log.info(f"No archive {_RAT_EXCLUDES_FILENAME} found, using 
temp_dir as scan root: {scan_root}")
@@ -345,9 +286,9 @@ def _check_core_logic(  # noqa: C901
             error_result, xml_output_path = _check_core_logic_execute_rat(
                 rat_jar_path, scan_root, temp_dir, effective_excludes_path, 
apply_extended_std, excludes_source
             )
-            if error_result:
-                error_result["excludes_source"] = excludes_source
-                error_result["extended_std_applied"] = apply_extended_std
+            if error_result is not None:
+                error_result.excludes_source = excludes_source
+                error_result.extended_std_applied = apply_extended_std
                 return error_result
 
             # Parse the XML output
@@ -356,45 +297,30 @@ def _check_core_logic(  # noqa: C901
             if xml_output_path is None:
                 raise ValueError("XML output path is None")
 
-            results = _check_core_logic_parse_output(xml_output_path, 
scan_root)
-            log.info(f"Successfully parsed RAT output with 
{util.plural(results.get('total_files', 0), 'file')}")
+            result = _check_core_logic_parse_output(xml_output_path, scan_root)
+            log.info(f"Successfully parsed RAT output with 
{util.plural(result.total_files, 'file')}")
 
-            # The unknown_license_files and unapproved_files keys contain 
lists of dicts
-            # {"name": "./README.md", "license": "Unknown license"}
+            # The unknown_license_files and unapproved_files contain FileEntry 
objects
             # The path is relative to scan_root, so we prepend the scan_root 
relative path
             scan_root_rel = os.path.relpath(scan_root, temp_dir)
             if scan_root_rel != ".":
-                for file in results["unknown_license_files"]:
-                    file["name"] = os.path.join(
-                        scan_root_rel,
-                        os.path.normpath(file["name"]),
-                    )
-                for file in results["unapproved_files"]:
-                    file["name"] = os.path.join(
-                        scan_root_rel,
-                        os.path.normpath(file["name"]),
-                    )
+                for file in result.unknown_license_files:
+                    file.name = os.path.join(scan_root_rel, 
os.path.normpath(file.name))
+                for file in result.unapproved_files:
+                    file.name = os.path.join(scan_root_rel, 
os.path.normpath(file.name))
 
-            results["excludes_source"] = excludes_source
-            results["extended_std_applied"] = apply_extended_std
-            return results
+            result.excludes_source = excludes_source
+            result.extended_std_applied = apply_extended_std
+            return result
 
     except Exception as e:
         import traceback
 
         log.exception("Error running Apache RAT")
-        return {
-            "valid": False,
-            "message": f"Failed to run Apache RAT: {e!s}",
-            "total_files": 0,
-            "approved_licenses": 0,
-            "unapproved_licenses": 0,
-            "unknown_licenses": 0,
-            "unapproved_files": [],
-            "unknown_license_files": [],
-            "errors": [str(e), traceback.format_exc()],
-            "excludes_source": "unknown",
-        }
+        return checkdata.Rat(
+            message=f"Failed to run Apache RAT: {e!s}",
+            errors=[str(e), traceback.format_exc()],
+        )
 
 
 def _check_core_logic_execute_rat(
@@ -404,7 +330,7 @@ def _check_core_logic_execute_rat(
     excludes_file_path: str | None,
     apply_extended_std: bool,
     excludes_source: str,
-) -> tuple[dict[str, Any] | None, str | None]:
+) -> tuple[checkdata.Rat | None, str | None]:
     """Execute Apache RAT and process its output."""
     xml_output_path = os.path.join(temp_dir, _RAT_REPORT_FILENAME)
     log.info(f"XML output will be written to: {xml_output_path}")
@@ -415,18 +341,11 @@ def _check_core_logic_execute_rat(
         abs_path = os.path.join(temp_dir, excludes_file_path)
         if not (os.path.exists(abs_path) and os.path.isfile(abs_path)):
             log.error(f"Exclusion file not found or not a regular file: 
{abs_path}")
-            return {
-                "valid": False,
-                "message": f"Exclusion file is not a regular file: 
{excludes_file_path}",
-                "total_files": 0,
-                "approved_licenses": 0,
-                "unapproved_licenses": 0,
-                "unknown_licenses": 0,
-                "unapproved_files": [],
-                "unknown_license_files": [],
-                "errors": [f"Expected exclusion file but found: {abs_path}"],
-                "excludes_source": excludes_source,
-            }, None
+            return checkdata.Rat(
+                message=f"Exclusion file is not a regular file: 
{excludes_file_path}",
+                errors=[f"Expected exclusion file but found: {abs_path}"],
+                excludes_source=excludes_source,
+            ), None
         excludes_file = os.path.relpath(abs_path, scan_root)
         log.info(f"Using exclusion file: {excludes_file} (source: 
{excludes_source})")
     command = _build_rat_command(rat_jar_path, xml_output_path, excludes_file, 
apply_extended_std)
@@ -439,10 +358,6 @@ def _check_core_logic_execute_rat(
     log.info(f"Executing Apache RAT from directory: {os.getcwd()}")
 
     try:
-        # # First make sure we can run Java
-        # java_check = subprocess.run(["java", "-version"], 
capture_output=True, timeout=10)
-        # log.info(f"Java check completed with return code 
{java_check.returncode}")
-
         # Run the actual RAT command
         # We do check=False because we'll handle errors below
         # The timeout is five minutes
@@ -459,57 +374,35 @@ def _check_core_logic_execute_rat(
             log.error(f"STDOUT: {process.stdout}")
             log.error(f"STDERR: {process.stderr}")
             os.chdir(current_dir)
-            error_dict = {
-                "valid": False,
-                "message": f"Apache RAT process failed with code 
{process.returncode}",
-                "total_files": 0,
-                "approved_licenses": 0,
-                "unapproved_licenses": 0,
-                "unknown_licenses": 0,
-                "unapproved_files": [],
-                "unknown_license_files": [],
-                "errors": [
+            return checkdata.Rat(
+                message=f"Apache RAT process failed with code 
{process.returncode}",
+                errors=[
                     f"Process error code: {process.returncode}",
                     f"STDOUT: {process.stdout}",
                     f"STDERR: {process.stderr}",
                 ],
-                "excludes_source": excludes_source,
-            }
-            return error_dict, None
+                excludes_source=excludes_source,
+            ), None
 
         log.info(f"Apache RAT completed successfully with return code 
{process.returncode}")
         log.info(f"stdout: {process.stdout[:200]}...")
     except subprocess.TimeoutExpired as e:
         os.chdir(current_dir)
         log.error(f"Apache RAT process timed out: {e}")
-        return {
-            "valid": False,
-            "message": "Apache RAT process timed out",
-            "total_files": 0,
-            "approved_licenses": 0,
-            "unapproved_licenses": 0,
-            "unknown_licenses": 0,
-            "unapproved_files": [],
-            "unknown_license_files": [],
-            "errors": [f"Timeout: {e}"],
-            "excludes_source": excludes_source,
-        }, None
+        return checkdata.Rat(
+            message="Apache RAT process timed out",
+            errors=[f"Timeout: {e}"],
+            excludes_source=excludes_source,
+        ), None
     except Exception as e:
         # Change back to the original directory before raising
         os.chdir(current_dir)
         log.error(f"Exception running Apache RAT: {e}")
-        return {
-            "valid": False,
-            "message": f"Apache RAT process failed: {e}",
-            "total_files": 0,
-            "approved_licenses": 0,
-            "unapproved_licenses": 0,
-            "unknown_licenses": 0,
-            "unapproved_files": [],
-            "unknown_license_files": [],
-            "errors": [f"Process error: {e}"],
-            "excludes_source": excludes_source,
-        }, None
+        return checkdata.Rat(
+            message=f"Apache RAT process failed: {e}",
+            errors=[f"Process error: {e}"],
+            excludes_source=excludes_source,
+        ), None
 
     # Change back to the original directory
     os.chdir(current_dir)
@@ -521,25 +414,18 @@ def _check_core_logic_execute_rat(
         log.info(f"Files in {temp_dir}: {os.listdir(temp_dir)}")
         # Look in the current directory too
         log.info(f"Files in current directory: {os.listdir('.')}")
-        return {
-            "valid": False,
-            "message": f"RAT output XML file not found: {xml_output_path}",
-            "total_files": 0,
-            "approved_licenses": 0,
-            "unapproved_licenses": 0,
-            "unknown_licenses": 0,
-            "unapproved_files": [],
-            "unknown_license_files": [],
-            "errors": [f"Missing output file: {xml_output_path}"],
-            "excludes_source": excludes_source,
-        }, None
+        return checkdata.Rat(
+            message=f"RAT output XML file not found: {xml_output_path}",
+            errors=[f"Missing output file: {xml_output_path}"],
+            excludes_source=excludes_source,
+        ), None
 
     # The XML was found correctly
     log.info(f"Found XML output at: {xml_output_path} (size: 
{os.path.getsize(xml_output_path)} bytes)")
     return None, xml_output_path
 
 
-def _check_core_logic_jar_exists(rat_jar_path: str) -> tuple[str, dict[str, 
Any] | None]:
+def _check_core_logic_jar_exists(rat_jar_path: str) -> tuple[str, 
checkdata.Rat | None]:
     """Verify that the Apache RAT JAR file exists and is accessible."""
     # Check that the RAT JAR exists
     if not os.path.exists(rat_jar_path):
@@ -572,42 +458,29 @@ def _check_core_logic_jar_exists(rat_jar_path: str) -> 
tuple[str, dict[str, Any]
             if os.path.exists("state"):
                 log.error(f"State directory contents: {os.listdir('state')}")
 
-            return rat_jar_path, {
-                "valid": False,
-                "message": f"Apache RAT JAR not found at: {rat_jar_path}",
-                "total_files": 0,
-                "approved_licenses": 0,
-                "unapproved_licenses": 0,
-                "unknown_licenses": 0,
-                "unapproved_files": [],
-                "unknown_license_files": [],
-                "errors": [f"Missing JAR: {rat_jar_path}"],
-                "excludes_source": "unknown",
-            }
+            return rat_jar_path, checkdata.Rat(
+                message=f"Apache RAT JAR not found at: {rat_jar_path}",
+                errors=[f"Missing JAR: {rat_jar_path}"],
+            )
     else:
         log.info(f"Found Apache RAT JAR at: {rat_jar_path}")
 
     return rat_jar_path, None
 
 
-def _check_core_logic_parse_output(xml_file: str, base_dir: str) -> dict[str, 
Any]:
+def _check_core_logic_parse_output(xml_file: str, base_dir: str) -> 
checkdata.Rat:
     """Parse the XML output from Apache RAT safely."""
     try:
         return _check_core_logic_parse_output_core(xml_file, base_dir)
     except Exception as e:
         log.error(f"Error parsing RAT output: {e}")
-        return {
-            "valid": False,
-            "message": f"Failed to parse Apache RAT output: {e!s}",
-            "total_files": 0,
-            "approved_licenses": 0,
-            "unapproved_licenses": 0,
-            "unknown_licenses": 0,
-            "errors": [f"XML parsing error: {e!s}"],
-        }
-
-
-def _check_core_logic_parse_output_core(xml_file: str, base_dir: str) -> 
dict[str, Any]:
+        return checkdata.Rat(
+            message=f"Failed to parse Apache RAT output: {e!s}",
+            errors=[f"XML parsing error: {e!s}"],
+        )
+
+
+def _check_core_logic_parse_output_core(xml_file: str, base_dir: str) -> 
checkdata.Rat:
     """Parse the XML output from Apache RAT."""
     tree = ElementTree.parse(xml_file)
     root = tree.getroot()
@@ -617,8 +490,8 @@ def _check_core_logic_parse_output_core(xml_file: str, 
base_dir: str) -> dict[st
     unapproved_licenses = 0
     unknown_licenses = 0
 
-    unapproved_files = []
-    unknown_license_files = []
+    unapproved_files: list[checkdata.RatFileEntry] = []
+    unknown_license_files: list[checkdata.RatFileEntry] = []
 
     # Process each resource
     for resource in root.findall(".//resource"):
@@ -640,7 +513,7 @@ def _check_core_logic_parse_output_core(xml_file: str, 
base_dir: str) -> dict[st
                 approved_licenses += 1
             else:
                 unknown_licenses += 1
-                unknown_license_files.append({"name": name, "license": 
"Unknown license"})
+                unknown_license_files.append(checkdata.RatFileEntry(name=name, 
license="Unknown license"))
         else:
             approval = license_elem.get("approval", "false")
             is_approved = approval == "true"
@@ -650,10 +523,10 @@ def _check_core_logic_parse_output_core(xml_file: str, 
base_dir: str) -> dict[st
                 approved_licenses += 1
             elif license_name == "Unknown license":
                 unknown_licenses += 1
-                unknown_license_files.append({"name": name, "license": 
license_name})
+                unknown_license_files.append(checkdata.RatFileEntry(name=name, 
license=license_name))
             else:
                 unapproved_licenses += 1
-                unapproved_files.append({"name": name, "license": 
license_name})
+                unapproved_files.append(checkdata.RatFileEntry(name=name, 
license=license_name))
 
     # Calculate overall validity
     valid = (unapproved_licenses == 0) and (unknown_licenses == 0)
@@ -662,20 +535,19 @@ def _check_core_logic_parse_output_core(xml_file: str, 
base_dir: str) -> dict[st
     message = _summary_message(valid, unapproved_licenses, unknown_licenses)
 
     # We limit the number of files we report to 100
-    return {
-        "valid": valid,
-        "message": message,
-        "total_files": total_files,
-        "approved_licenses": approved_licenses,
-        "unapproved_licenses": unapproved_licenses,
-        "unknown_licenses": unknown_licenses,
-        "unapproved_files": unapproved_files[:100],
-        "unknown_license_files": unknown_license_files[:100],
-        "errors": [],
-    }
-
-
-def _check_java_installed() -> dict[str, Any] | None:
+    return checkdata.Rat(
+        valid=valid,
+        message=message,
+        total_files=total_files,
+        approved_licenses=approved_licenses,
+        unapproved_licenses=unapproved_licenses,
+        unknown_licenses=unknown_licenses,
+        unapproved_files=unapproved_files[:100],
+        unknown_license_files=unknown_license_files[:100],
+    )
+
+
+def _check_java_installed() -> checkdata.Rat | None:
     # Check that Java is installed
     # TODO: Run this only once, when the server starts
     try:
@@ -706,18 +578,10 @@ def _check_java_installed() -> dict[str, Any] | None:
         except Exception as inner_e:
             log.error(f"Additional error while trying to debug java: 
{inner_e}")
 
-        return {
-            "valid": False,
-            "message": "Java is not properly installed or not in PATH",
-            "total_files": 0,
-            "approved_licenses": 0,
-            "unapproved_licenses": 0,
-            "unknown_licenses": 0,
-            "unapproved_files": [],
-            "unknown_license_files": [],
-            "errors": [f"Java error: {e}"],
-            "excludes_source": "unknown",
-        }
+        return checkdata.Rat(
+            message="Java is not properly installed or not in PATH",
+            errors=[f"Java error: {e}"],
+        )
 
 
 def _count_files_outside_directory(temp_dir: str, scan_root: str) -> int:
diff --git a/tests/unit/test_checks_rat.py b/tests/unit/test_checks_rat.py
index 97f3440..b47cf94 100644
--- a/tests/unit/test_checks_rat.py
+++ b/tests/unit/test_checks_rat.py
@@ -44,10 +44,10 @@ def _skip_if_unavailable(rat_available: tuple[bool, bool]) 
-> None:
 def test_check_includes_excludes_source_none(rat_available: tuple[bool, bool]):
     _skip_if_unavailable(rat_available)
     result = rat._check_core_logic(str(TEST_ARCHIVE), [])
-    assert result["excludes_source"] == "none"
+    assert result.excludes_source == "none"
 
 
 def test_check_includes_excludes_source_policy(rat_available: tuple[bool, 
bool]):
     _skip_if_unavailable(rat_available)
     result = rat._check_core_logic(str(TEST_ARCHIVE), ["*.py"])
-    assert result["excludes_source"] == "policy"
+    assert result.excludes_source == "policy"


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to