This commit is contained in:
valenti
2026-09-08 07:21:34 -04:00
commit a4b224da8e
169 changed files with 27483 additions and 0 deletions
@@ -0,0 +1,27 @@
"""asf-connectors: Protocol-based interfaces for every external system ASF
can integrate with.
Concrete implementations (GitHub adapter, Jira adapter, ...) are provided
separately, often in their own installable extras. Business code MUST
depend only on the Protocols defined here. This keeps ASF Core decoupled
from any single provider.
"""
__version__ = "0.1.0"
from asf_connectors.git import GitConnector, PullRequestInfo, CommitInfo
from asf_connectors.issues import IssueTrackerConnector, IssueInfo
from asf_connectors.ci import CICDConnector, BuildInfo, DeploymentInfo
from asf_connectors.observability import ObservabilityConnector
__all__ = [
"GitConnector",
"PullRequestInfo",
"CommitInfo",
"IssueTrackerConnector",
"IssueInfo",
"CICDConnector",
"BuildInfo",
"DeploymentInfo",
"ObservabilityConnector",
]
@@ -0,0 +1,42 @@
"""Abstract CI/CD connector interface."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import Protocol, Optional, Sequence
@dataclass(frozen=True)
class BuildInfo:
build_id: str
pipeline_name: str
status: str # success | failed | running | cancelled
started_at: Optional[datetime] = None
finished_at: Optional[datetime] = None
triggered_by: Optional[str] = None
branch: Optional[str] = None
commit_sha: Optional[str] = None
url: Optional[str] = None
@dataclass(frozen=True)
class DeploymentInfo:
deployment_id: str
environment: str # dev | staging | prod | ...
status: str # success | failed | in_progress
version_ref: Optional[str] = None
started_at: Optional[datetime] = None
finished_at: Optional[datetime] = None
target: Optional[str] = None
url: Optional[str] = None
class CICDConnector(Protocol):
"""Jenkins / GitLab CI / GitHub Actions / ArgoCD / ..."""
async def get_build(self, pipeline: str, build_id: str) -> Optional[BuildInfo]: ...
async def list_builds(self, pipeline: str, limit: int = 50) -> Sequence[BuildInfo]: ...
async def get_deployment(self, deployment_id: str) -> Optional[DeploymentInfo]: ...
async def list_deployments(self, environment: str, limit: int = 50) -> Sequence[DeploymentInfo]: ...
@@ -0,0 +1,57 @@
"""Abstract Git (repository / source-control) connector interface."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import Protocol, AsyncIterator, Optional, Sequence
@dataclass(frozen=True)
class CommitInfo:
sha: str
message: str
author_email: str
author_name: str
authored_at: datetime
branch: Optional[str] = None
files_changed: Sequence[str] = ()
@dataclass(frozen=True)
class PullRequestInfo:
number: int
title: str
state: str # open | closed | merged
branch: str
target_branch: str
author: str
created_at: datetime
merged_at: Optional[datetime] = None
description: Optional[str] = None
commit_shas: Sequence[str] = ()
labels: Sequence[str] = ()
class GitConnector(Protocol):
"""Any Git-hosting provider: GitHub, GitLab, Bitbucket, local Gitea..."""
async def list_commits(
self, repo_ref: str, *, branch: Optional[str] = None, since: Optional[datetime] = None
) -> AsyncIterator[CommitInfo]: ...
async def get_commit(self, repo_ref: str, sha: str) -> Optional[CommitInfo]: ...
async def list_pull_requests(
self, repo_ref: str, *, state: str = "open"
) -> AsyncIterator[PullRequestInfo]: ...
async def get_pull_request(self, repo_ref: str, number: int) -> Optional[PullRequestInfo]: ...
async def index_repository(self, repo_ref: str) -> None:
"""Trigger a full sync (artifacts + relationships) for a repo.
Implementation must emit events; it must NOT mutate Core data
directly.
"""
...
@@ -0,0 +1,34 @@
"""Abstract issue / requirement tracker connector (Jira, Linear, etc.)."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import Protocol, AsyncIterator, Optional, Any, Sequence
@dataclass(frozen=True)
class IssueInfo:
key: str
title: str
type: str # epic, story, bug, task, ...
status: str
description: Optional[str] = None
assignee: Optional[str] = None
labels: Sequence[str] = ()
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
parent_key: Optional[str] = None
metadata: dict[str, Any] = None # type: ignore[assignment]
class IssueTrackerConnector(Protocol):
"""Provider interface for Jira / GitHub Issues / YouTrack / ..."""
async def search_issues(
self, query: str, *, project_ref: Optional[str] = None, limit: int = 100
) -> AsyncIterator[IssueInfo]: ...
async def get_issue(self, issue_key: str) -> Optional[IssueInfo]: ...
async def sync_project(self, project_ref: str) -> None: ...
@@ -0,0 +1,20 @@
"""Abstract observability connector (Grafana / Prometheus / Datadog / ...)."""
from __future__ import annotations
from datetime import datetime
from typing import Protocol, Optional, Any
class ObservabilityConnector(Protocol):
async def query_metric(
self,
query: str,
start: datetime,
end: datetime,
step: Optional[str] = None,
) -> list[dict[str, Any]]: ...
async def get_incident(self, incident_id: str) -> Optional[dict[str, Any]]: ...
async def list_active_incidents(self, limit: int = 50) -> list[dict[str, Any]]: ...