From 021d837a30176fef3ce9c523357b7071197c1d44 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Mon, 4 May 2026 19:17:31 -0700 Subject: [PATCH 1/4] ci: align ruff tooling and add CI workflow The .pre-commit-config.yaml was pinning ruff v0.5.0 (mid-2024) while the dev-dep ruff resolved to v0.15.11. The two versions disagree on isort grouping (newer ruff splits pydantic and openarmature into separate import groups; v0.5.0 merges them), which caused test files to ping-pong between forms on every commit cycle. Bump pre-commit ruff to v0.15.11 to match the dev dep, then run `ruff check --fix` once to settle the disagreement on the newer form. Eight test-file imports reordered as a result. Add `.github/workflows/ci.yml` running: - Checkout with submodules (the openarmature-spec submodule carries the conformance fixtures) - uv sync --frozen - ruff check - ruff format --check - pyright src/ tests/ - pytest -q Same checks the pre-commit hook runs locally, now also enforced server-side so PRs from contributors who haven't installed pre-commit don't bypass them. Concurrency group cancels in-flight runs on the same ref. --- .github/workflows/ci.yml | 45 +++++++++++++++++++++++++++ .pre-commit-config.yaml | 2 +- tests/conformance/adapter.py | 2 ++ tests/conformance/test_conformance.py | 1 + tests/unit/test_compile_errors.py | 1 + tests/unit/test_generics.py | 3 +- tests/unit/test_projection.py | 3 +- tests/unit/test_runtime_errors.py | 3 +- tests/unit/test_state_and_reducers.py | 3 +- tests/unit/test_subgraph.py | 3 +- 10 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9650978 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,45 @@ +name: CI + +on: + pull_request: + branches: [main] + push: + branches: [main] + +# 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 + + - name: Install Python + run: uv python install 3.12 + + - 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..a51de39 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, diff --git a/tests/conformance/test_conformance.py b/tests/conformance/test_conformance.py index 0bfd6e7..72a5835 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, 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): From 05ddf838cb1e7bfadd1540feb3422db380a5c877 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Mon, 4 May 2026 19:27:12 -0700 Subject: [PATCH 2/4] ci: add explicit permissions block for least-privilege Mirrors LunarCommand/openarmature-examples#3: pin GITHUB_TOKEN to `contents: read`. The workflow only needs to checkout code + the spec submodule; no writes. --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9650978..a47d0e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,11 @@ on: 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 }} From cc6a13df595c9b693532ed5a234909eb4754c2e1 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Mon, 4 May 2026 19:47:48 -0700 Subject: [PATCH 3/4] ci: apply v0.15.11 ordering to files added since rebase base Post-rebase: ruff v0.15.11 splits pydantic and openarmature into separate import groups, where v0.5.0 (the version on main when #5 landed) merged them. The bumped pre-commit hook in this branch applies the new ordering to test files brought in by #5. --- tests/conformance/adapter.py | 1 - tests/conformance/test_conformance.py | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/conformance/adapter.py b/tests/conformance/adapter.py index a51de39..67759fb 100644 --- a/tests/conformance/adapter.py +++ b/tests/conformance/adapter.py @@ -34,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 72a5835..76b429f 100644 --- a/tests/conformance/test_conformance.py +++ b/tests/conformance/test_conformance.py @@ -140,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}" + ) # --------------------------------------------------------------------------- From 73e70378c2dc98aa7eff987898e25a7604e07f65 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Mon, 4 May 2026 19:53:11 -0700 Subject: [PATCH 4/4] ci: drop explicit Python install, use .python-version Per PR #6 review: the workflow had `uv python install 3.12` while .python-version pins 3.14, creating a CI/dev skew where CI could end up testing a different interpreter than developers run locally. Drop the explicit install step. uv sync auto-provisions whatever .python-version pins, keeping CI and dev aligned. requires-python ">=3.12" in pyproject.toml still establishes 3.12 as the floor; testing the floor specifically is a separate concern (matrix build) we can add later if needed. --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a47d0e2..adbd9b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,8 +31,9 @@ jobs: with: enable-cache: true - - name: Install Python - run: uv python install 3.12 + # 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