Skip to content
Open
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
2 changes: 1 addition & 1 deletion compatibility/consumers/atlas_click/src/atlas_click/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

from __future__ import annotations

import click
import base_cli
import click


@click.group(name="atlas-consumer", help="Inventory resources managed by Atlas.")
Expand Down
1 change: 0 additions & 1 deletion compatibility/consumers/atlas_click/tests/test_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from pathlib import Path

import base_cli

from atlas_click.cli import command


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import base_cli
import typer


cli = typer.Typer(help="Deploy Beacon services with typed parameters.")


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from pathlib import Path

import base_cli

from beacon_typer.cli import command


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

from __future__ import annotations

import click
import base_cli
from typing import Any

import base_cli
import click

app = base_cli.App(
name="cinder-consumer",
Expand All @@ -27,7 +28,7 @@
default="json",
show_default=True,
)
def reconcile(ctx: base_cli.Context, target: str, output_format: str) -> None:
def reconcile(ctx: base_cli.Context[Any, Any, Any], target: str, output_format: str) -> None:
"""Publish the result of one idempotent reconciliation step."""

action = "would-reconcile" if ctx.dry_run else "reconciled"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from pathlib import Path

import base_cli

from cinder_automation.cli import app


Expand Down
8 changes: 8 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,11 @@ The full gate writes a machine-readable result to
`$BASE_CLI_VALIDATION_RESULT` (or `/tmp/base-cli-validation-result.json`). If
Node.js is unavailable, the result is marked `partial`, the gate exits with
status `2`, and it cannot be reported as an authoritative pass.

### Consumer source quality

The style gate runs Ruff over the entire repository with its standard generated-file
exclusions. The typing gate checks every Git-visible Python source outside `lib/`
(checked separately), `scripts/` (validation tools), and `tests/` (test harnesses)
with strict mypy. This includes example and compatibility consumer packages and
new top-level source directories; untracked sources are included during development.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from __future__ import annotations

from typing import Any

import base_cli
import click

Expand Down Expand Up @@ -35,7 +37,7 @@
)
@base_cli.option("--api-token", hidden=True, help="Optional secret for a real adapter.")
def run(
ctx: base_cli.Context,
ctx: base_cli.Context[Any, Any, Any],
target: str,
output_format: str,
api_token: str | None,
Expand Down
4 changes: 3 additions & 1 deletion examples/minimal_cli/src/minimal_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from __future__ import annotations

from typing import Any

import base_cli

app = base_cli.App(
Expand All @@ -13,7 +15,7 @@

@app.command()
@base_cli.option("--name", required=True, help="Name to greet.")
def greet(ctx: base_cli.Context, name: str) -> None:
def greet(ctx: base_cli.Context[Any, Any, Any], name: str) -> None:
"""Print a deterministic greeting."""

ctx.log.info("greeting requested for %s", name)
Expand Down
46 changes: 46 additions & 0 deletions scripts/validate_consumer_typing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""Type-check every example and compatibility consumer at strict settings."""

from __future__ import annotations

import os
import subprocess
import sys
from pathlib import Path


def main() -> int:
root = Path(__file__).resolve().parents[1]
# Git discovery also includes new, untracked source directories. Infrastructure
# scripts/tests have separate runtime gates; all product/example code is typed.
result = subprocess.run(
["git", "ls-files", "--cached", "--others", "--exclude-standard", "--", "*.py"],
cwd=root,
check=True,
capture_output=True,
text=True,
)
files = sorted(set(result.stdout.splitlines()))
excluded = {"lib", "scripts", "tests"}
sources = [name for name in files if Path(name).parts[0] not in excluded]
if not sources:
raise RuntimeError("No consumer Python sources found")
return subprocess.run(
[sys.executable, "-m", "mypy", "--strict", "--explicit-package-bases", *sources],
cwd=root,
env={
**os.environ,
"MYPYPATH": os.pathsep.join(
str(p)
for p in [
root / "lib/python",
*sorted(root.glob("examples/*/src")),
*sorted(root.glob("compatibility/consumers/*/src")),
]
),
},
check=False,
).returncode


if __name__ == "__main__":
raise SystemExit(main())
7 changes: 5 additions & 2 deletions tests/full_validate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,15 @@ run_typing() {
require_commands python mypy
python -m mypy --strict examples/typed_consumer.py
python -m mypy --strict lib/python/base_cli
# Discover all Python sources so new top-level directories cannot escape checks.
python scripts/validate_consumer_typing.py
}

run_style() {
require_commands ruff
ruff format --check lib/python/base_cli scripts examples tests
ruff check lib/python/base_cli scripts examples tests
# Markdown examples are validated by the dedicated documentation gate.
ruff format --check --exclude "*.md" .
ruff check .
}

run_contracts() {
Expand Down
Loading