From 986741e2dff5d69b30807cf870ac791295741c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Sun, 19 Jul 2026 10:13:59 +0200 Subject: [PATCH 1/2] ci: add an advisory Python 3.15 pre-release leg to the matrix (#126) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPython 3.15 is final on 2026-10-01; 3.15.0-beta.3 is already in the setup-python manifest. The leg runs continue-on-error so a pre-release regression reports without blocking a merge, and allow-prereleases only falls back to a pre-release while a version line has no GA build — so when 3.15.0 ships the same pin resolves to it with no edit; only the continue-on-error line and the trove classifier are left to flip. The supported floor is untouched: >=3.10 for all of 1.x (the raise to >=3.11 belongs to the 2.0 window, #74). No CHANGELOG entry: CI-only, the published artifact is unchanged — matching the precedent set when 3.14 joined the matrix. Closes #126 --- .github/workflows/ci.yml | 9 ++++++++- CONTRIBUTING.md | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b165fa..ef785d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,10 +22,15 @@ jobs: name: test (py${{ matrix.python-version }}${{ matrix.pyld != 'latest' && format(', pyld {0}', matrix.pyld) || '' }}) runs-on: ubuntu-latest timeout-minutes: 15 + # 3.15 is still a pre-release (final 2026-10-01), so its leg is advisory: it + # reports but never blocks a merge. Drop this line when 3.15.0 ships and the + # trove classifier goes in (#126) — the version pin below needs no edit, since + # allow-prereleases only falls back to a pre-release while no GA build exists. + continue-on-error: ${{ matrix.python-version == '3.15' }} strategy: fail-fast: false matrix: - python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] + python-version: ['3.10', '3.11', '3.12', '3.13', '3.14', '3.15'] pyld: ['latest'] include: # The [data-integrity] floor edge (#124): oldest supported pyld on the @@ -41,6 +46,8 @@ jobs: uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} + # Only consulted when a version line has no GA build yet (today: 3.15). + allow-prereleases: true cache: pip - name: Install (core + ebsi + dev) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bda09fb..a76722b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,8 @@ mypy # type check (strict-ish; the code is fully typed) gitlint # commit-message convention (see below) ``` -CI runs the same on Python 3.10–3.14. +CI runs the same on Python 3.10–3.14, plus an advisory 3.15 pre-release leg (it +reports but never blocks) and a leg pinning the oldest supported `pyld`. ## Docs From 5c0f34db49ff3d547ad3dcf42e9df89268f50edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Sun, 19 Jul 2026 10:19:53 +0200 Subject: [PATCH 2/2] ci: install only the wheel-available extras on the 3.15 leg; fix mypy override MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first run of the new leg failed at install: lxml (pulled by signxml and by pyld 3.x) publishes no cp315 wheel, so pip fell back to building it from source and died for want of libxml2 headers. That is an ecosystem lag in an optional transitive dep, not an openvc signal — and a permanently-red advisory leg teaches everyone to ignore CI. The leg now installs [ebsi,dev] instead of [all], so it actually exercises openvc's own hand-rolled core (JOSE, CBOR/COSE, codecs, status lists, DIDs) on the new interpreter: 1028 of 1107 tests run, the rest importorskip. Coverage on that subset is 82%, so the leg is held to the local gate's floor of 80 rather than the full-matrix 85. Verified in a venv built with exactly those extras. That same venv surfaced a latent config bug: signxml was missing from mypy's ignore_missing_imports overrides, so `mypy` failed for ANYONE working without the [trustlist] extra installed, not just on 3.15. Added; mypy is now clean both with and without the optional extras. --- .github/workflows/ci.yml | 28 ++++++++++++++++++++++------ CONTRIBUTING.md | 9 +++++++-- pyproject.toml | 2 +- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef785d8..81ac6a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,25 +19,38 @@ concurrency: jobs: test: - name: test (py${{ matrix.python-version }}${{ matrix.pyld != 'latest' && format(', pyld {0}', matrix.pyld) || '' }}) + name: test (py${{ matrix.python-version }}${{ matrix.pyld != 'latest' && format(', pyld {0}', matrix.pyld) || '' }}${{ matrix.extras != 'all' && ', pre-release' || '' }}) runs-on: ubuntu-latest timeout-minutes: 15 # 3.15 is still a pre-release (final 2026-10-01), so its leg is advisory: it # reports but never blocks a merge. Drop this line when 3.15.0 ships and the - # trove classifier goes in (#126) — the version pin below needs no edit, since + # trove classifier goes in (#126) — the version pin needs no edit, since # allow-prereleases only falls back to a pre-release while no GA build exists. continue-on-error: ${{ matrix.python-version == '3.15' }} strategy: fail-fast: false matrix: - python-version: ['3.10', '3.11', '3.12', '3.13', '3.14', '3.15'] + python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] pyld: ['latest'] + extras: ['all'] include: # The [data-integrity] floor edge (#124): oldest supported pyld on the # oldest Python; every other job resolves the newest pyld (3.x). The # golden fixtures are the canonicalization-drift alarm on both edges. - python-version: '3.10' pyld: '2.0.4' + extras: 'all' + # Pre-release watch (#126). Installs core + ebsi + dev, NOT [all]: + # lxml — pulled in by signxml and by pyld 3.x — publishes no cp315 + # wheel yet, and building it from source would test lxml's build, not + # openvc. The DI / trustlist / schema suites therefore importorskip + # here (~1028 of 1107 tests still run, hence the lower coverage floor + # below). What this leg does watch is the hand-rolled, attacker-facing + # core — JOSE, CBOR/COSE, the codecs, status lists, DID resolution — + # on the new interpreter. Widen to 'all' once lxml ships cp315 wheels. + - python-version: '3.15' + pyld: 'latest' + extras: 'ebsi,dev' steps: - uses: actions/checkout@v7 @@ -50,10 +63,10 @@ jobs: allow-prereleases: true cache: pip - - name: Install (core + ebsi + dev) + - name: Install (core + [${{ matrix.extras }}]) run: | python -m pip install --upgrade pip - pip install -e ".[all]" + pip install -e ".[${{ matrix.extras }}]" if [ "${{ matrix.pyld }}" != "latest" ]; then pip install "pyld==${{ matrix.pyld }}"; fi - name: Lint (flake8) @@ -65,9 +78,12 @@ jobs: # Offline tier only: the live EBSI smoke test stays opt-in # (OPENVC_EBSI_LIVE=1) so CI never depends on an external service. - name: Test (pytest + coverage) + # The pre-release leg skips the pyld/signxml/jsonschema-gated suites, so it + # is held to the local gate's floor (80) instead of the full-matrix 85. run: >- pytest -q --cov=openvc --cov=openvc_ebsi - --cov-report=term-missing --cov-fail-under=85 + --cov-report=term-missing + --cov-fail-under=${{ matrix.extras == 'all' && '85' || '80' }} publish: name: Publish to PyPI diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a76722b..7ce1dd4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,8 +17,13 @@ mypy # type check (strict-ish; the code is fully typed) gitlint # commit-message convention (see below) ``` -CI runs the same on Python 3.10–3.14, plus an advisory 3.15 pre-release leg (it -reports but never blocks) and a leg pinning the oldest supported `pyld`. +CI runs the same on Python 3.10–3.14, plus a leg pinning the oldest supported +`pyld` and an advisory 3.15 pre-release leg (it reports but never blocks, and +installs `[ebsi,dev]` only — `lxml` has no cp315 wheel yet, so the suites behind +`[data-integrity]`, `[trustlist]` and `[schema]` skip themselves there). + +Working without an extra installed is supported: those test suites +`importorskip`, and `mypy` ignores the missing optional imports. ## Docs diff --git a/pyproject.toml b/pyproject.toml index 0964d12..0a03c00 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,5 +115,5 @@ no_implicit_optional = true # Third-party deps behind optional extras that ship no type information. [[tool.mypy.overrides]] module = ["httpx", "httpx.*", "pyld", "pyld.*", "jsonschema", "jsonschema.*", - "referencing", "referencing.*", "lxml", "lxml.*"] + "referencing", "referencing.*", "lxml", "lxml.*", "signxml", "signxml.*"] ignore_missing_imports = true