import json from dataclasses import dataclass from typing import Dict, FrozenSet, List, Optional, Protocol, Tuple, TypedDict, Union JsonValue = Union[None, bool, int, float, str, List["JsonValue"], Dict[str, "JsonValue"]] class JsonLoader(Protocol): def __call__(self, s: str) -> JsonValue: ... def decode_json(value: str) -> JsonValue: def load(loader: JsonLoader) -> JsonValue: return loader(value) return load(json.loads) class SummaryJson(TypedDict): scanned_runs: int downloaded_artifacts: int rejected_artifacts: int detected_occurrences: int creates: int comments: int reopens: int noops: int conflicts: int exit_code: int @dataclass(frozen=True) class Testcase: classname: str name: str result: str flaky_retries: int failed_retries: int @dataclass(frozen=True) class Report: attempt: int testcases: Tuple[Testcase, ...] ambiguous: FrozenSet[Tuple[str, str]] @dataclass(frozen=True) class Occurrence: repository: str workflow_path: str workflow_name: str run_id: int final_attempt: int run_url: str occurred_at: str classname: str testcase_name: str modes: FrozenSet[str] fingerprint: str @dataclass(frozen=True) class Issue: number: int state: str body: str comments: Tuple[str, ...] closed_at: Optional[str] labels: Tuple[str, ...] @dataclass(frozen=True) class Action: kind: str key: str issue_number: Optional[int] title: str chunks: Tuple[str, ...] previous_closed_at: Optional[str] @dataclass(frozen=True) class Workflow: id: int @dataclass(frozen=True) class PullRequest: base_ref: Optional[str] @dataclass(frozen=True) class WorkflowRun: id: int status: str run_attempt: int event: str head_branch: Optional[str] pull_requests: Tuple[PullRequest, ...] updated_at: str run_started_at: str html_url: str name: str path: Optional[str] @dataclass(frozen=True) class WorkflowRuns: total_count: int runs: Tuple[WorkflowRun, ...] @dataclass(frozen=True) class Artifact: id: int name: str expired: bool @dataclass(frozen=True) class ArtifactPage: total_count: int artifacts: Tuple[Artifact, ...] @dataclass(frozen=True) class RemoteComment: body: Optional[str] @dataclass(frozen=True) class RemoteIssue: number: int state: str body: Optional[str] closed_at: Optional[str] labels: Tuple[str, ...] is_pull_request: bool @dataclass(frozen=True) class CreatedIssue: number: int @dataclass class RejectedArtifact: id: int rejected_at: str kind: str = "whole" @dataclass class PendingRecurrence: issue_number: int previous_closed_at: Optional[str] fingerprints: List[str] chunks: List[str] @dataclass class GovernanceState: schema_version: int successful_scan_started_at: Optional[str] pending_recurrences: List[PendingRecurrence] rejected_artifacts: List[RejectedArtifact] @dataclass class Summary: scanned_runs: int = 0 downloaded_artifacts: int = 0 rejected_artifacts: int = 0 detected_occurrences: int = 0 creates: int = 0 comments: int = 0 reopens: int = 0 noops: int = 0 conflicts: int = 0 exit_code: int = 0 state_path: Optional[str] = None def as_json(self) -> SummaryJson: return { "scanned_runs": self.scanned_runs, "downloaded_artifacts": self.downloaded_artifacts, "rejected_artifacts": self.rejected_artifacts, "detected_occurrences": self.detected_occurrences, "creates": self.creates, "comments": self.comments, "reopens": self.reopens, "noops": self.noops, "conflicts": self.conflicts, "exit_code": self.exit_code, }