merge master into ariadne hygiene branch

This commit is contained in:
codex 2026-04-21 01:07:44 -03:00
commit 7f284007eb
4 changed files with 83 additions and 33 deletions

View File

@ -57,12 +57,18 @@ class KeycloakOIDC:
def _decode_claims(self, token: str, key: dict[str, Any]) -> dict[str, Any]: def _decode_claims(self, token: str, key: dict[str, Any]) -> dict[str, Any]:
return jwt.decode( return jwt.decode(
token, token,
key=jwt.algorithms.RSAAlgorithm.from_jwk(key), key=self._key_from_jwk(key),
algorithms=["RS256"], algorithms=["RS256"],
options={"verify_aud": False}, options={"verify_aud": False},
issuer=self._issuer, issuer=self._issuer,
) )
def _key_from_jwk(self, key: dict[str, Any]) -> Any:
algorithm = getattr(jwt.algorithms, "RSAAlgorithm", None)
if algorithm and hasattr(algorithm, "from_jwk"):
return algorithm.from_jwk(key)
return jwt.PyJWK.from_dict(key).key
def _validate_audience(self, claims: dict[str, Any]) -> None: def _validate_audience(self, claims: dict[str, Any]) -> None:
azp = claims.get("azp") azp = claims.get("azp")
aud = claims.get("aud") aud = claims.get("aud")

View File

@ -8,29 +8,45 @@ import ast
from pathlib import Path from pathlib import Path
def _is_dataclass_class(node: ast.ClassDef) -> bool:
"""Return whether a class uses the dataclass decorator."""
return any(
(isinstance(dec, ast.Name) and dec.id == "dataclass")
or (isinstance(dec, ast.Call) and isinstance(dec.func, ast.Name) and dec.func.id == "dataclass")
for dec in node.decorator_list
)
def _base_names(node: ast.ClassDef) -> set[str]:
"""Return simple base class names used by a class definition."""
return {base.id for base in node.bases if isinstance(base, ast.Name)}
def _needs_function_docstring(node: ast.FunctionDef | ast.AsyncFunctionDef, parent_class: str | None) -> bool:
"""Return whether a public function-like node needs a docstring."""
if node.name.startswith("_") and node.name != "__init__":
return False
return not (parent_class and node.name.startswith("_"))
def _needs_class_docstring(node: ast.ClassDef) -> bool:
"""Return whether a public class-like node needs a docstring."""
bases = _base_names(node)
skipped_bases = {"Exception", "RuntimeError", "BaseException", "BaseModel"}
return not (node.name.startswith("_") or _is_dataclass_class(node) or bool(bases.intersection(skipped_bases)))
def _needs_docstring(node: ast.AST, *, parent_class: str | None = None) -> bool: def _needs_docstring(node: ast.AST, *, parent_class: str | None = None) -> bool:
"""Return whether `node` should carry an API contract docstring.""" """Return whether `node` should carry an API contract docstring."""
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
name = node.name return _needs_function_docstring(node, parent_class)
if name.startswith("_") and name != "__init__":
return False
return not (parent_class and name.startswith("_"))
if isinstance(node, ast.ClassDef): if isinstance(node, ast.ClassDef):
if node.name.startswith("_"): return _needs_class_docstring(node)
return False
if any(
(isinstance(dec, ast.Name) and dec.id == "dataclass")
or (isinstance(dec, ast.Call) and isinstance(dec.func, ast.Name) and dec.func.id == "dataclass")
for dec in node.decorator_list
):
return False
if any(
isinstance(base, ast.Name) and base.id in {"Exception", "RuntimeError", "BaseException"}
for base in node.bases
):
return False
return not any(isinstance(base, ast.Name) and base.id == "BaseModel" for base in node.bases)
return False return False

View File

@ -204,11 +204,31 @@ def _supply_chain_check_status(build_dir: Path) -> str:
return "failed" return "failed"
def _resolve_artifact_paths(repo_root: Path) -> tuple[Path, Path]:
"""Find coverage and JUnit artifacts even when a test runner uses fallback names."""
coverage_path = Path(os.getenv("COVERAGE_JSON", "build/coverage.json"))
junit_path = Path(os.getenv("JUNIT_XML", "build/junit.xml"))
if not coverage_path.exists():
for candidate in (
repo_root / "build" / "coverage.json",
repo_root / "build" / "coverage-summary.json",
repo_root / "build" / "coverage" / "coverage-summary.json",
):
if candidate.exists():
coverage_path = candidate
break
if not junit_path.exists():
junit_candidates = sorted((repo_root / "build").glob("junit*.xml"))
if junit_candidates:
junit_path = junit_candidates[0]
return coverage_path, junit_path
def main() -> int: def main() -> int:
repo_root = Path(__file__).resolve().parents[1] repo_root = Path(__file__).resolve().parents[1]
build_dir = repo_root / "build" build_dir = repo_root / "build"
coverage_path = os.getenv("COVERAGE_JSON", "build/coverage.json") coverage_path, junit_path = _resolve_artifact_paths(repo_root)
junit_path = os.getenv("JUNIT_XML", "build/junit.xml")
pushgateway_url = os.getenv( pushgateway_url = os.getenv(
"PUSHGATEWAY_URL", "http://platform-quality-gateway.monitoring.svc.cluster.local:9091" "PUSHGATEWAY_URL", "http://platform-quality-gateway.monitoring.svc.cluster.local:9091"
).strip() ).strip()
@ -217,16 +237,19 @@ def main() -> int:
build_number = os.getenv("BUILD_NUMBER", "") build_number = os.getenv("BUILD_NUMBER", "")
commit = os.getenv("GIT_COMMIT", "") commit = os.getenv("GIT_COMMIT", "")
print(f"[metrics] coverage_path={coverage_path} exists={coverage_path.exists()}")
print(f"[metrics] junit_path={junit_path} exists={junit_path.exists()}")
coverage = 0.0 coverage = 0.0
if os.path.exists(coverage_path): if coverage_path.exists():
coverage = _load_coverage(coverage_path) coverage = _load_coverage(str(coverage_path))
docs_gate_rc = _load_gate_rc(Path(os.getenv("QUALITY_GATE_DOCS_RC_PATH", str(build_dir / "docs-naming.rc")))) docs_gate_rc = _load_gate_rc(Path(os.getenv("QUALITY_GATE_DOCS_RC_PATH", str(build_dir / "docs-naming.rc"))))
source_lines_over_500 = _count_source_files_over_limit(repo_root, max_lines=500) source_lines_over_500 = _count_source_files_over_limit(repo_root, max_lines=500)
totals = {"tests": 0, "failures": 0, "errors": 0, "skipped": 0} totals = {"tests": 0, "failures": 0, "errors": 0, "skipped": 0}
test_cases: list[tuple[str, str]] = [] test_cases: list[tuple[str, str]] = []
if os.path.exists(junit_path): if junit_path.exists():
totals = _load_junit(junit_path) totals = _load_junit(str(junit_path))
test_cases = _load_junit_cases(junit_path) test_cases = _load_junit_cases(str(junit_path))
passed = max(totals["tests"] - totals["failures"] - totals["errors"] - totals["skipped"], 0) passed = max(totals["tests"] - totals["failures"] - totals["errors"] - totals["skipped"], 0)
outcome = "ok" outcome = "ok"
@ -284,10 +307,15 @@ def main() -> int:
"# TYPE ariadne_quality_gate_build_info gauge", "# TYPE ariadne_quality_gate_build_info gauge",
f"ariadne_quality_gate_build_info{_label_str(labels)} 1", f"ariadne_quality_gate_build_info{_label_str(labels)} 1",
] ]
if test_cases:
payload_lines.extend( payload_lines.extend(
f'platform_quality_gate_test_case_result{{suite="{suite}",test="{_escape_label(test_name)}",status="{_escape_label(test_status)}"}} 1' f'platform_quality_gate_test_case_result{{suite="{suite}",test="{_escape_label(test_name)}",status="{_escape_label(test_status)}"}} 1'
for test_name, test_status in test_cases for test_name, test_status in test_cases
) )
else:
payload_lines.append(
f'platform_quality_gate_test_case_result{{suite="{suite}",test="__no_test_cases__",status="skipped"}} 1'
)
payload_lines.extend( payload_lines.extend(
f'ariadne_quality_gate_checks_total{{suite="{suite}",check="{check_name}",result="{check_status}"}} 1' f'ariadne_quality_gate_checks_total{{suite="{suite}",check="{check_name}",result="{check_status}"}} 1'
for check_name, check_status in checks.items() for check_name, check_status in checks.items()

View File

@ -20,7 +20,7 @@ def test_keycloak_verify_accepts_matching_audience(monkeypatch) -> None:
kc = KeycloakOIDC("https://jwks", "https://issuer", "portal") kc = KeycloakOIDC("https://jwks", "https://issuer", "portal")
monkeypatch.setattr(kc, "_get_jwks", lambda force=False: {"keys": [{"kid": "test"}]}) monkeypatch.setattr(kc, "_get_jwks", lambda force=False: {"keys": [{"kid": "test"}]})
monkeypatch.setattr(jwt.algorithms.RSAAlgorithm, "from_jwk", lambda key: "dummy") monkeypatch.setattr(kc, "_key_from_jwk", lambda key: "dummy")
monkeypatch.setattr( monkeypatch.setattr(
jwt, jwt,
"decode", "decode",
@ -36,7 +36,7 @@ def test_keycloak_verify_rejects_wrong_audience(monkeypatch) -> None:
kc = KeycloakOIDC("https://jwks", "https://issuer", "portal") kc = KeycloakOIDC("https://jwks", "https://issuer", "portal")
monkeypatch.setattr(kc, "_get_jwks", lambda force=False: {"keys": [{"kid": "test"}]}) monkeypatch.setattr(kc, "_get_jwks", lambda force=False: {"keys": [{"kid": "test"}]})
monkeypatch.setattr(jwt.algorithms.RSAAlgorithm, "from_jwk", lambda key: "dummy") monkeypatch.setattr(kc, "_key_from_jwk", lambda key: "dummy")
monkeypatch.setattr( monkeypatch.setattr(
jwt, jwt,
"decode", "decode",
@ -73,7 +73,7 @@ def test_keycloak_verify_refreshes_jwks(monkeypatch) -> None:
return {"keys": [{"kid": "test"}]} return {"keys": [{"kid": "test"}]}
monkeypatch.setattr(kc, "_get_jwks", fake_get_jwks) monkeypatch.setattr(kc, "_get_jwks", fake_get_jwks)
monkeypatch.setattr(jwt.algorithms.RSAAlgorithm, "from_jwk", lambda key: "dummy") monkeypatch.setattr(kc, "_key_from_jwk", lambda key: "dummy")
monkeypatch.setattr( monkeypatch.setattr(
jwt, jwt,
"decode", "decode",