first
This commit is contained in:
@@ -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]]: ...
|
||||
@@ -0,0 +1,20 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=68", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "asf-connectors"
|
||||
version = "0.1.0"
|
||||
description = "ASF external-system connector interfaces (Protocol-based, no concrete adapters yet)."
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"asf-domain>=0.1.0",
|
||||
"asf-events>=0.1.0",
|
||||
"pydantic>=2.7,<3",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["pytest>=8"]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["asf_connectors*"]
|
||||
Reference in New Issue
Block a user