Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions .github/workflows/compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,17 @@ jobs:
python -m pip install dist/*.whl
- name: Validate consumer manifest
run: python scripts/validate_consumers.py
- name: Install consumers independently
- name: Install consumers without dependency resolution
run: |
python -m pip install compatibility/consumers/atlas_click
python -m pip install compatibility/consumers/beacon_typer
python -m pip install "compatibility/consumers/cinder_automation[observability]"
python -m pip install --no-deps compatibility/consumers/atlas_click
python -m pip install --no-deps compatibility/consumers/beacon_typer
python -m pip install --no-deps "compatibility/consumers/cinder_automation[observability]"
- name: Verify the installed framework matches the source release
run: |
expected="$(tr -d '\r\n' < VERSION)"
actual="$(python -c 'from importlib.metadata import version; print(version("base-cli"))')"
test "$actual" = "$expected"
python -m pip check
- name: Run downstream compatibility suites
run: |
for tests in compatibility/consumers/*/tests; do
Expand Down
31 changes: 22 additions & 9 deletions compatibility/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,43 @@ outcomes.

`scripts/validate_consumers.py` validates the manifest, package metadata, and
required compatibility documentation. The `Reference consumers` workflow
builds and installs the base-cli wheel first, installs each consumer with its
own dependencies, and runs each consumer's tests. This catches import,
packaging, adapter, and contract regressions without relying on repository
source imports.
builds and installs the base-cli wheel first, installs each consumer without
dependency resolution, verifies that the installed distribution still matches
the source release, and runs each consumer's tests. This prevents pip from
silently replacing the release candidate with an older published wheel.

The downstream job records a dated JSON result as the
`base-cli-compatibility-evidence-<run-id>` artifact. It binds the result to the
framework revision and version and lists the exact fixture and matrix that
passed. See [`adoption-evidence.md`](../docs/adoption-evidence.md) for the
claim and permission boundary.
passed. The record remains `schema_version: 1`; `source_version` identifies
the checked-out `VERSION`, while `framework_version` identifies the installed
distribution. These are additive metadata fields, so readers should not reject
the record merely because a newer record contains additional fields. See
[`adoption-evidence.md`](../docs/adoption-evidence.md) for the claim and
permission boundary.

The same workflow runs the Typer adapter and Beacon fixture against Typer
0.25.1, 0.26.0, and 0.27.1 on Python 3.10 through 3.14. This matrix covers the
transition from Click's public command classes to Typer's vendored Click fork.
0.25.1, 0.26.0, 0.27.1, and 0.27.2 on Python 3.10 through 3.14. This matrix
covers the transition from Click's public command classes to Typer's vendored
Click fork.

Run the same checks locally:

```bash
python scripts/validate_consumers.py
python -m build --wheel
python -m pip install dist/base_cli-*.whl
for consumer in compatibility/consumers/*; do python -m pip install "$consumer"; done
for consumer in compatibility/consumers/*; do python -m pip install --no-deps "$consumer"; done
python -m pip check
for tests in compatibility/consumers/*/tests; do python -m pytest "$tests"; done
```

The fixture metadata targets `base-cli>=0.5,<0.6`. Dependency-resolving
installation therefore requires the 0.5 package to be available from the
configured index. The workflow instead installs the reviewed wheel first,
installs each fixture with `--no-deps`, verifies the installed framework
version, and then runs `pip check` so the compatibility result cannot silently
fall back to an older published framework.

The fixtures are not customer claims. A permissioned public adopter can be
added as a separate record while retaining the same downstream contract tests.
3 changes: 2 additions & 1 deletion compatibility/consumers/atlas_click/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ without rebuilding the Click tree or changing the inventory output contract.

Install with `python -m pip install .`, run `atlas-consumer --help`, and execute
`atlas-consumer --quiet inventory`. The package is pinned to the supported
`base-cli` 0.3 minor window; tests run against the installed wheel in CI.
`base-cli` 0.5 minor window; tests run against the installed release-candidate
wheel in CI.
2 changes: 1 addition & 1 deletion compatibility/consumers/atlas_click/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "base-cli-compat-consumer-atlas"
version = "0.1.0"
description = "Independent Click consumer compatibility fixture for base-cli"
requires-python = ">=3.10"
dependencies = ["base-cli>=0.3,<0.5", "click>=8.1"]
dependencies = ["base-cli>=0.5,<0.6", "click>=8.1,<8.6"]

[project.scripts]
atlas-consumer = "atlas_click.cli:main"
Expand Down
9 changes: 5 additions & 4 deletions compatibility/consumers/beacon_typer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ proves that `base_cli.attach_typer()` preserves Typer's generated help,
validation, and callback behavior while adding the `base-cli` framework lifecycle.

Install with `python -m pip install .`, run `beacon-consumer --help`, and execute
`beacon-consumer --quiet deploy --service api --replicas 3`. The supported Typer
range is explicit in package metadata and is exercised by the compatibility
workflow across Typer 0.25.1, 0.26.0, and 0.27.1 on Python 3.10 through 3.14.
Run `python -m pytest tests` to repeat the downstream tests locally.
`beacon-consumer --quiet deploy --service api --replicas 3`. The package targets
the `base-cli` 0.5 minor window. The supported Typer range is explicit in
package metadata and is exercised by the compatibility workflow across Typer
0.25.1, 0.26.0, 0.27.1, and 0.27.2 on Python 3.10 through 3.14. Run
`python -m pytest tests` to repeat the downstream tests locally.
2 changes: 1 addition & 1 deletion compatibility/consumers/beacon_typer/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "base-cli-compat-consumer-beacon"
version = "0.1.0"
description = "Independent Typer consumer compatibility fixture for base-cli"
requires-python = ">=3.10"
dependencies = ["base-cli>=0.3,<0.5", "typer>=0.25.1,<0.28"]
dependencies = ["base-cli>=0.5,<0.6", "typer>=0.25.1,<0.28"]

[project.scripts]
beacon-consumer = "beacon_typer.cli:main"
Expand Down
1 change: 1 addition & 0 deletions compatibility/consumers/cinder_automation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ Install with `python -m pip install '.[observability]'`, run
`cinder-consumer --help`, and start with
`cinder-consumer --quiet --dry-run --target warehouse`. Exporters are optional;
the command remains successful when no provider is configured.
The package targets the supported `base-cli` 0.5 minor window.
Run `python -m pytest tests` to repeat the dry-run and JSON contract tests.
2 changes: 1 addition & 1 deletion compatibility/consumers/cinder_automation/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "base-cli-compat-consumer-cinder"
version = "0.1.0"
description = "Independent automation consumer compatibility fixture for base-cli"
requires-python = ">=3.10"
dependencies = ["base-cli>=0.3,<0.5", "click>=8.1"]
dependencies = ["base-cli>=0.5,<0.6", "click>=8.1,<8.6"]

[project.optional-dependencies]
observability = ["opentelemetry-api>=1.24,<2"]
Expand Down
18 changes: 17 additions & 1 deletion scripts/record_compatibility_evidence.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import os
import subprocess
from datetime import datetime, timezone
from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as distribution_version
from pathlib import Path
from typing import Any

Expand All @@ -30,14 +32,28 @@ def _version() -> str:
return (ROOT / "VERSION").read_text(encoding="utf-8").splitlines()[0].strip()


def _installed_version() -> str:
try:
return distribution_version("base-cli")
except PackageNotFoundError as exc:
raise SystemExit("compatibility evidence requires an installed base-cli distribution") from exc


def main() -> None:
now = datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
source_version = _version()
installed_version = _installed_version()
if installed_version != source_version:
raise SystemExit(
f"compatibility evidence version mismatch: source {source_version}, installed {installed_version}"
)
record: dict[str, Any] = {
"schema_version": 1,
"status": "passed",
"recorded_at": now,
"source_revision": _revision(),
"framework_version": _version(),
"source_version": source_version,
"framework_version": installed_version,
"workflow": os.environ.get("GITHUB_WORKFLOW", "local"),
"run_id": os.environ.get("GITHUB_RUN_ID"),
"run_attempt": os.environ.get("GITHUB_RUN_ATTEMPT"),
Expand Down
Loading