This is an automated email from the ASF dual-hosted git repository. arm pushed a commit to branch check_version_database in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
commit 905e9234c9de39ce8158ce608bae468cb1eeb344 Author: Alastair McFarlane <[email protected]> AuthorDate: Wed Mar 18 13:53:16 2026 +0000 #892 - record check version in database (and fix a couple of type issues in tests) --- atr/models/sql.py | 2 ++ atr/tasks/checks/__init__.py | 11 ++++++++++ atr/tasks/checks/compare.py | 1 + atr/tasks/checks/hashing.py | 1 + atr/tasks/checks/license.py | 2 ++ atr/tasks/checks/paths.py | 4 ++++ atr/tasks/checks/rat.py | 1 + atr/tasks/checks/signature.py | 1 + atr/tasks/checks/targz.py | 1 + atr/tasks/checks/zipformat.py | 1 + migrations/versions/0061_2026.03.18_7838cfcc.py | 27 +++++++++++++++++++++++++ tests/unit/recorders.py | 11 ++++++---- tests/unit/test_checks_compare.py | 10 ++++++--- 13 files changed, 66 insertions(+), 7 deletions(-) diff --git a/atr/models/sql.py b/atr/models/sql.py index 7f677449..d0a1a817 100644 --- a/atr/models/sql.py +++ b/atr/models/sql.py @@ -1015,6 +1015,8 @@ class CheckResult(sqlmodel.SQLModel, table=True): # We don't call this latest_revision_number, because it might not be the latest revision_number: str | None = sqlmodel.Field(default=None, index=True, **example("00005")) checker: str = sqlmodel.Field(**example("atr.tasks.checks.license.files")) + checker_version: str | None = sqlmodel.Field(default=None, **example("2")) + primary_rel_path: str | None = sqlmodel.Field( default=None, index=True, **example("apache-example-0.0.1-source.tar.gz") ) diff --git a/atr/tasks/checks/__init__.py b/atr/tasks/checks/__init__.py index 0b8beaff..acefedfa 100644 --- a/atr/tasks/checks/__init__.py +++ b/atr/tasks/checks/__init__.py @@ -61,6 +61,7 @@ class FunctionArguments: class Recorder: checker: str + checker_version: str | None release_key: safe.ReleaseKey project_key: safe.ProjectKey version_key: safe.VersionKey @@ -74,6 +75,7 @@ class Recorder: def __init__( self, checker: str | Callable[..., Any], + checker_version: str | None, inputs_hash: str | None, project_key: safe.ProjectKey, version_key: safe.VersionKey, @@ -83,6 +85,7 @@ class Recorder: afresh: bool = True, ) -> None: self.checker = function_key(checker) + self.checker_version = checker_version self.release_key = sql.release_key(project_key, version_key) self.revision_number = revision_number self.primary_rel_path = primary_rel_path @@ -100,6 +103,7 @@ class Recorder: async def create( cls, checker: str | Callable[..., Any], + checker_version: str | None, inputs_hash: str, project_key: safe.ProjectKey, version_key: safe.VersionKey, @@ -110,6 +114,7 @@ class Recorder: ) -> Recorder: recorder = cls( checker, + checker_version, inputs_hash, project_key, version_key, @@ -134,6 +139,8 @@ class Recorder: ) -> sql.CheckResult: if self.constructed is False: raise RuntimeError("Cannot add check result to a recorder that has not been constructed") + if self.checker_version is None: + raise RuntimeError("checker_version must be set before recording results") if primary_rel_path is not None: if self.primary_rel_path is not None: raise ValueError("Cannot specify path twice") @@ -149,6 +156,7 @@ class Recorder: release_key=str(self.release_key), revision_number=str(self.revision_number), checker=self.checker, + checker_version=self.checker_version, primary_rel_path=primary_rel_path or self.primary_rel_path, member_rel_path=member_rel_path, created=datetime.datetime.now(datetime.UTC), @@ -243,6 +251,9 @@ class Recorder: def input_hash(self) -> str | None: return self.__input_hash + def set_checker_version(self, version: str) -> None: + self.checker_version = version + async def blocker( self, message: str, diff --git a/atr/tasks/checks/compare.py b/atr/tasks/checks/compare.py index 73264b64..1c6fe0c3 100644 --- a/atr/tasks/checks/compare.py +++ b/atr/tasks/checks/compare.py @@ -84,6 +84,7 @@ class TreeComparisonResult: async def source_trees(args: checks.FunctionArguments) -> results.Results | None: # noqa: C901 recorder = await args.recorder() + recorder.set_checker_version(CHECK_VERSION) is_source = await recorder.primary_path_is_source() if not is_source: log.info( diff --git a/atr/tasks/checks/hashing.py b/atr/tasks/checks/hashing.py index dd11a427..063f2422 100644 --- a/atr/tasks/checks/hashing.py +++ b/atr/tasks/checks/hashing.py @@ -34,6 +34,7 @@ CHECK_VERSION: Final[str] = "1" async def check(args: checks.FunctionArguments) -> results.Results | None: """Check the hash of a file.""" recorder = await args.recorder() + recorder.set_checker_version(CHECK_VERSION) if not (hash_abs_path := await recorder.abs_path()): return None diff --git a/atr/tasks/checks/license.py b/atr/tasks/checks/license.py index 722e160f..a39a715b 100644 --- a/atr/tasks/checks/license.py +++ b/atr/tasks/checks/license.py @@ -132,6 +132,7 @@ type Result = ArtifactResult | MemberResult | MemberSkippedResult async def files(args: checks.FunctionArguments) -> results.Results | None: """Check that the LICENSE and NOTICE files exist and are valid.""" recorder = await args.recorder() + recorder.set_checker_version(CHECK_VERSION_FILES) if not (artifact_abs_path := await recorder.abs_path()): return None @@ -173,6 +174,7 @@ async def files(args: checks.FunctionArguments) -> results.Results | None: async def headers(args: checks.FunctionArguments) -> results.Results | None: """Check that all source files have valid license headers.""" recorder = await args.recorder() + recorder.set_checker_version(CHECK_VERSION_HEADERS) if not (artifact_abs_path := await recorder.abs_path()): return None diff --git a/atr/tasks/checks/paths.py b/atr/tasks/checks/paths.py index d127403f..9c33abf6 100644 --- a/atr/tasks/checks/paths.py +++ b/atr/tasks/checks/paths.py @@ -53,9 +53,11 @@ async def check(args: checks.FunctionArguments) -> results.Results | None: # - Incubation Policy (IP) # https://incubator.apache.org/policy/incubation.html base_recorder = await args.recorder() + base_recorder.set_checker_version(CHECK_VERSION) recorder_errors = await checks.Recorder.create( checker=checks.function_key(check) + "_errors", + checker_version=CHECK_VERSION, inputs_hash=base_recorder.input_hash or "", project_key=args.project_key, version_key=args.version_key, @@ -65,6 +67,7 @@ async def check(args: checks.FunctionArguments) -> results.Results | None: ) recorder_warnings = await checks.Recorder.create( checker=checks.function_key(check) + "_warnings", + checker_version=CHECK_VERSION, inputs_hash=base_recorder.input_hash or "", project_key=args.project_key, version_key=args.version_key, @@ -74,6 +77,7 @@ async def check(args: checks.FunctionArguments) -> results.Results | None: ) recorder_success = await checks.Recorder.create( checker=checks.function_key(check) + "_success", + checker_version=CHECK_VERSION, inputs_hash=base_recorder.input_hash or "", project_key=args.project_key, version_key=args.version_key, diff --git a/atr/tasks/checks/rat.py b/atr/tasks/checks/rat.py index acb1d474..1ea8a6e0 100644 --- a/atr/tasks/checks/rat.py +++ b/atr/tasks/checks/rat.py @@ -78,6 +78,7 @@ class RatError(RuntimeError): async def check(args: checks.FunctionArguments) -> results.Results | None: """Use Apache RAT to check the licenses of the files in the artifact.""" recorder = await args.recorder() + recorder.set_checker_version(CHECK_VERSION) if not (artifact_abs_path := await recorder.abs_path()): return None if await recorder.primary_path_is_binary(): diff --git a/atr/tasks/checks/signature.py b/atr/tasks/checks/signature.py index 050f5ae5..92af5b2a 100644 --- a/atr/tasks/checks/signature.py +++ b/atr/tasks/checks/signature.py @@ -39,6 +39,7 @@ CHECK_VERSION: Final[str] = "1" async def check(args: checks.FunctionArguments) -> results.Results | None: """Check a signature file.""" recorder = await args.recorder() + recorder.set_checker_version(CHECK_VERSION) if not (primary_abs_path := await recorder.abs_path()): return None diff --git a/atr/tasks/checks/targz.py b/atr/tasks/checks/targz.py index dee2988d..61824592 100644 --- a/atr/tasks/checks/targz.py +++ b/atr/tasks/checks/targz.py @@ -76,6 +76,7 @@ def root_directory(archive_dir: pathlib.Path) -> tuple[str, bytes | None]: async def structure(args: checks.FunctionArguments) -> results.Results | None: # noqa: C901 """Check the structure of a .tar.gz file using the extracted tree.""" recorder = await args.recorder() + recorder.set_checker_version(CHECK_VERSION_STRUCTURE) if not (artifact_abs_path := await recorder.abs_path()): return None if await recorder.primary_path_is_binary(): diff --git a/atr/tasks/checks/zipformat.py b/atr/tasks/checks/zipformat.py index defe2dbc..fe029e27 100644 --- a/atr/tasks/checks/zipformat.py +++ b/atr/tasks/checks/zipformat.py @@ -39,6 +39,7 @@ async def structure(args: checks.FunctionArguments) -> results.Results | None: # For simplicity, they've been updated separately for now # (There are several small differences to resolve between the two) recorder = await args.recorder() + recorder.set_checker_version(CHECK_VERSION_STRUCTURE) if not (artifact_abs_path := await recorder.abs_path()): return None if await recorder.primary_path_is_binary(): diff --git a/migrations/versions/0061_2026.03.18_7838cfcc.py b/migrations/versions/0061_2026.03.18_7838cfcc.py new file mode 100644 index 00000000..e05c5ff9 --- /dev/null +++ b/migrations/versions/0061_2026.03.18_7838cfcc.py @@ -0,0 +1,27 @@ +"""check version into database + +Revision ID: 0061_2026.03.18_7838cfcc +Revises: 0060_2026.03.16_2c8e4716 +Create Date: 2026-03-18 13:51:12.776504+00:00 +""" + +from collections.abc import Sequence + +import sqlalchemy as sa +from alembic import op + +# Revision identifiers, used by Alembic +revision: str = "0061_2026.03.18_7838cfcc" +down_revision: str | None = "0060_2026.03.16_2c8e4716" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + with op.batch_alter_table("checkresult", schema=None) as batch_op: + batch_op.add_column(sa.Column("checker_version", sa.String(), nullable=True)) + + +def downgrade() -> None: + with op.batch_alter_table("checkresult", schema=None) as batch_op: + batch_op.drop_column("checker_version") diff --git a/tests/unit/recorders.py b/tests/unit/recorders.py index 96b5c09b..2dcb67be 100644 --- a/tests/unit/recorders.py +++ b/tests/unit/recorders.py @@ -20,18 +20,20 @@ import pathlib from collections.abc import Awaitable, Callable from typing import Any +import atr.models.safe as safe import atr.models.sql as sql import atr.tasks.checks as checks class RecorderStub(checks.Recorder): - def __init__(self, path: pathlib.Path, checker: str) -> None: + def __init__(self, path: pathlib.Path, checker: str, checker_version: str | None = None) -> None: super().__init__( checker=checker, + checker_version=checker_version, inputs_hash=None, - project_key="test", - version_key="test", - revision_number="00001", + project_key=safe.ProjectKey("test"), + version_key=safe.VersionKey("test"), + revision_number=safe.RevisionNumber("00001"), primary_rel_path=None, member_rel_path=None, afresh=False, @@ -59,6 +61,7 @@ class RecorderStub(checks.Recorder): release_key=self.release_key, revision_number=self.revision_number, checker=self.checker, + checker_version=self.checker_version, primary_rel_path=primary_rel_path, member_rel_path=member_rel_path, created=datetime.datetime.now(datetime.UTC), diff --git a/tests/unit/test_checks_compare.py b/tests/unit/test_checks_compare.py index 229f3928..f87984b8 100644 --- a/tests/unit/test_checks_compare.py +++ b/tests/unit/test_checks_compare.py @@ -27,6 +27,7 @@ import dulwich.refs import pytest import atr.models.github +import atr.models.safe import atr.models.sql import atr.tasks.checks import atr.tasks.checks.compare @@ -201,10 +202,11 @@ class RecorderStub(atr.tasks.checks.Recorder): def __init__(self, is_source: bool) -> None: super().__init__( checker="compare.source_trees", + checker_version="1", inputs_hash=None, - project_key="project", - version_key="version", - revision_number="00001", + project_key=atr.models.safe.ProjectKey("project"), + version_key=atr.models.safe.VersionKey("version"), + revision_number=atr.models.safe.RevisionNumber("00001"), primary_rel_path="artifact.tar.gz", member_rel_path=None, afresh=False, @@ -224,6 +226,7 @@ class RecorderStub(atr.tasks.checks.Recorder): release_key=self.release_key, revision_number=self.revision_number, checker=self.checker, + checker_version=self.checker_version, primary_rel_path=primary_rel_path or self.primary_rel_path, member_rel_path=member_rel_path, created=datetime.datetime.now(datetime.UTC), @@ -241,6 +244,7 @@ class RecorderStub(atr.tasks.checks.Recorder): release_key=None, revision_number=None, checker=self.checker, + checker_version=self.checker_version, primary_rel_path=primary_rel_path or self.primary_rel_path, member_rel_path=member_rel_path, created=datetime.datetime.now(datetime.UTC), --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
