diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d46e254..a9a2767 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,16 +5,18 @@ name: Publish on: - push: - tags: - - 'v*' - paths-ignore: - - '**.md' + workflow_run: + workflows: + - Build + - Validation + types: + - completed jobs: publish: name: Publish Python Ort on PyPI runs-on: ubuntu-24.04 + if: github.event.workflow_run.conclusion == 'success' && startsWith(github.event.workflow_run.head_branch, 'v') environment: name: PyPI permissions: diff --git a/examples/model_relation_test.py b/examples/model_relation_test.py new file mode 100644 index 0000000..18cad5a --- /dev/null +++ b/examples/model_relation_test.py @@ -0,0 +1,20 @@ +import inspect + +from pydantic import BaseModel + +import ort.models # or your top-level models package + + +def iter_models(module): + for _, obj in inspect.getmembers(module): + if inspect.isclass(obj) and issubclass(obj, BaseModel): + yield obj + + +for model in iter_models(ort.models): + try: + print("Rebuilding:", model) + model.model_rebuild() + except Exception: + print("FAILED:", model) + raise diff --git a/pyproject.toml b/pyproject.toml index 62744d5..122e744 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "uv_build" [project] name = "python-ort" -version = "0.6.4" +version = "0.6.5" description = "A Python Ort model serialization library" readme = "README.md" license = "MIT" diff --git a/src/ort/models/config/issue_resolution.py b/src/ort/models/config/issue_resolution.py index 1e93e2b..9e37bac 100644 --- a/src/ort/models/config/issue_resolution.py +++ b/src/ort/models/config/issue_resolution.py @@ -2,7 +2,9 @@ # SPDX-License-Identifier: MIT -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, field_validator + +from ort.utils import convert_enum from .issue_resolution_reason import IssueResolutionReason @@ -30,3 +32,8 @@ class IssueResolution(BaseModel): comment: str = Field( description="A comment to further explain why the [reason] is applicable here.", ) + + @field_validator("reason", mode="before") + @classmethod + def validate_reason(cls, value): + return convert_enum(IssueResolutionReason, value) diff --git a/src/ort/models/config/vulnerability_resolution.py b/src/ort/models/config/vulnerability_resolution.py index a9a9942..f995233 100644 --- a/src/ort/models/config/vulnerability_resolution.py +++ b/src/ort/models/config/vulnerability_resolution.py @@ -2,7 +2,9 @@ # SPDX-License-Identifier: MIT -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, field_validator + +from ort.utils import convert_enum from .vulnerability_resolution_reason import VulnerabilityResolutionReason @@ -29,3 +31,8 @@ class VulnerabilityResolution(BaseModel): comment: str = Field( description="A comment to further explain why the [reason] is applicable here.", ) + + @field_validator("reason", mode="before") + @classmethod + def validate_reason(cls, value): + return convert_enum(VulnerabilityResolutionReason, value) diff --git a/src/ort/models/scope.py b/src/ort/models/scope.py index a3545a6..0afa3bf 100644 --- a/src/ort/models/scope.py +++ b/src/ort/models/scope.py @@ -28,3 +28,11 @@ class Scope(BaseModel): "testing the product. But transitive dependencies would not be test dependencies of the test " "dependencies, but compile dependencies of test dependencies.", ) + + def __hash__(self) -> int: + return hash(self.name) + + def __eq__(self, other) -> bool: + if not isinstance(other, Scope): + return NotImplemented + return self.name == other.name diff --git a/tests/data/analyzer-result.yml b/tests/data/analyzer-result.yml index 6917353..7cd4e5f 100644 --- a/tests/data/analyzer-result.yml +++ b/tests/data/analyzer-result.yml @@ -1534,6 +1534,7 @@ analyzer: url: "https://github.com/pypa/virtualenv.git" revision: "" path: "" + scopes: [] dependency_graphs: PIP: packages: diff --git a/tests/data/repo_config/str_boolean.ort.yml b/tests/data/repo_config/str_boolean.ort.yml index 2616169..a5e1743 100644 --- a/tests/data/repo_config/str_boolean.ort.yml +++ b/tests/data/repo_config/str_boolean.ort.yml @@ -22,3 +22,14 @@ excludes: - pattern: '**/doc/**' reason: OTHER comment: Documentation not in scan scope +resolutions: + issues: + - message: "IOException: Could not resolve provenance for package 'Maven:com.google.*" + reason: CANT_FIX_ISSUE + comment: "Google is not publishing sources for all their android libraries, especially the ones from the play-services or MLKit" + - message: "IOException: Could not resolve provenance for package 'Maven:com.phrase.*" + reason: CANT_FIX_ISSUE + comment: "Phrase is licensed by a proprietary license and the sources of their libraries aren't published." + - message: "IOException: Could not resolve provenance for package 'Maven:org.jetbrains.kotlin:kotlin-stdlib-common:.*" + reason: CANT_FIX_ISSUE + comment: "The 'org.jetbrains.kotlin:kotlin-stdlib-common' library is a metadata library for which no sources are published by JetBrains" diff --git a/tests/test_repo_config_files.py b/tests/test_repo_config_files.py new file mode 100644 index 0000000..ca68ce9 --- /dev/null +++ b/tests/test_repo_config_files.py @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2026 Helio Chissini de Castro +# SPDX-License-Identifier: MIT + +from pathlib import Path + +import pytest +import yaml +from pydantic import ValidationError + +from ort.models.config.repository_configuration import RepositoryConfiguration + +REPO_CONFIG_DIR = Path(__file__).parent / "data" / "repo_config" + +# Collect all YAML files that are expected to be valid RepositoryConfiguration documents +_VALID_FILES = [f for f in REPO_CONFIG_DIR.glob("*.yml") if f.name != "only_include_reason_fail.yml"] + + +@pytest.mark.parametrize("config_file", _VALID_FILES, ids=lambda f: f.name) +def test_repo_config_file_loads_without_validation_error(config_file: Path) -> None: + """Each file in tests/data/repo_config (except known-invalid ones) must load + into RepositoryConfiguration without raising a pydantic ValidationError.""" + data = yaml.safe_load(config_file.read_text()) + try: + RepositoryConfiguration.model_validate(data or {}) + except ValidationError as exc: + pytest.fail(f"{config_file.name} raised ValidationError: {exc}") diff --git a/uv.lock b/uv.lock index c8f1d31..189d5d0 100644 --- a/uv.lock +++ b/uv.lock @@ -575,7 +575,7 @@ wheels = [ [[package]] name = "python-ort" -version = "0.6.4" +version = "0.6.5" source = { editable = "." } dependencies = [ { name = "packageurl-python" },