diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..adbd9b1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI + +on: + pull_request: + branches: [main] + push: + branches: [main] + +# Least-privilege: the workflow only needs to read code (checkout + +# submodule). No writes anywhere. +permissions: + contents: read + +# Cancel in-flight runs on the same ref when a new push lands. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # Conformance fixtures live in the openarmature-spec submodule. + submodules: recursive + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + enable-cache: true + + # No explicit `uv python install` — `uv sync` auto-provisions the + # interpreter pinned in `.python-version`, so CI tests the same + # version developers run locally. + + - name: Sync deps + run: uv sync --frozen + + - name: Lint (ruff check) + run: uv run ruff check . + + - name: Format check (ruff format --check) + run: uv run ruff format --check . + + - name: Type check (pyright) + run: uv run pyright src/ tests/ + + - name: Run tests (pytest) + run: uv run pytest -q diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 31ad8b7..c4d5a2e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: - id: check-added-large-files - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.0 + rev: v0.15.11 hooks: - id: ruff args: [--fix] diff --git a/tests/conformance/adapter.py b/tests/conformance/adapter.py index a4a6bca..67759fb 100644 --- a/tests/conformance/adapter.py +++ b/tests/conformance/adapter.py @@ -15,6 +15,8 @@ from dataclasses import dataclass, field from typing import TYPE_CHECKING, Annotated, Any, cast +from pydantic import Field, create_model + from openarmature.graph import ( END, CompiledGraph, @@ -32,7 +34,6 @@ ) from openarmature.graph.events import NodeEvent from openarmature.graph.observer import Observer -from pydantic import Field, create_model if TYPE_CHECKING: from openarmature.graph.observer import _InvocationContext diff --git a/tests/conformance/test_conformance.py b/tests/conformance/test_conformance.py index 0bfd6e7..76b429f 100644 --- a/tests/conformance/test_conformance.py +++ b/tests/conformance/test_conformance.py @@ -12,6 +12,7 @@ import pytest import yaml + from openarmature.graph import ( CompileError, NodeException, @@ -139,15 +140,15 @@ async def test_runtime_fixture(fixture_path: Path) -> None: for name, expected_events in expected["observer_events"].items(): actual = observer_fixtures[name].events normalized = [normalize_expected_event(ev) for ev in expected_events] - assert ( - actual == normalized - ), f"observer events mismatch for {name!r}: actual={actual}, expected={normalized}" + assert actual == normalized, ( + f"observer events mismatch for {name!r}: actual={actual}, expected={normalized}" + ) if "delivery_order" in expected: expected_delivery = [(d["observer"], d["step"]) for d in expected["delivery_order"]] - assert ( - delivery == expected_delivery - ), f"delivery_order mismatch: actual={delivery}, expected={expected_delivery}" + assert delivery == expected_delivery, ( + f"delivery_order mismatch: actual={delivery}, expected={expected_delivery}" + ) # --------------------------------------------------------------------------- diff --git a/tests/unit/test_compile_errors.py b/tests/unit/test_compile_errors.py index 91b07ce..cf6b987 100644 --- a/tests/unit/test_compile_errors.py +++ b/tests/unit/test_compile_errors.py @@ -3,6 +3,7 @@ from typing import Any import pytest + from openarmature.graph import ( END, DanglingEdge, diff --git a/tests/unit/test_generics.py b/tests/unit/test_generics.py index ba53de8..fc9f51a 100644 --- a/tests/unit/test_generics.py +++ b/tests/unit/test_generics.py @@ -11,6 +11,8 @@ from typing import Annotated, Any, assert_type +from pydantic import Field + from openarmature.graph import ( END, CompiledGraph, @@ -20,7 +22,6 @@ State, append, ) -from pydantic import Field class ParentS(State): diff --git a/tests/unit/test_projection.py b/tests/unit/test_projection.py index 5243ad0..c3f03ad 100644 --- a/tests/unit/test_projection.py +++ b/tests/unit/test_projection.py @@ -1,13 +1,14 @@ """Projection strategy unit coverage: FieldNameMatching + ExplicitMapping.""" import pytest +from pydantic import Field + from openarmature.graph import ( ExplicitMapping, FieldNameMatching, MappingReferencesUndeclaredField, State, ) -from pydantic import Field class Parent(State): diff --git a/tests/unit/test_runtime_errors.py b/tests/unit/test_runtime_errors.py index 7ddc6fd..e780687 100644 --- a/tests/unit/test_runtime_errors.py +++ b/tests/unit/test_runtime_errors.py @@ -10,6 +10,8 @@ from typing import Annotated, Any import pytest +from pydantic import Field + from openarmature.graph import ( END, EdgeException, @@ -20,7 +22,6 @@ StateValidationError, append, ) -from pydantic import Field class S(State): diff --git a/tests/unit/test_state_and_reducers.py b/tests/unit/test_state_and_reducers.py index e644c87..85c3fea 100644 --- a/tests/unit/test_state_and_reducers.py +++ b/tests/unit/test_state_and_reducers.py @@ -3,9 +3,10 @@ from typing import Annotated import pytest +from pydantic import Field, ValidationError + from openarmature.graph import END, State, append, last_write_wins, merge from openarmature.graph.state import field_reducers, resolve_reducer -from pydantic import Field, ValidationError class S(State): diff --git a/tests/unit/test_subgraph.py b/tests/unit/test_subgraph.py index a211fbd..dafa76b 100644 --- a/tests/unit/test_subgraph.py +++ b/tests/unit/test_subgraph.py @@ -8,6 +8,8 @@ from typing import Annotated, Any +from pydantic import Field + from openarmature.graph import ( END, FieldNameMatching, @@ -16,7 +18,6 @@ SubgraphNode, append, ) -from pydantic import Field class Inner(State):