ai-sdlc-framework 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ai_sdlc_framework-0.1.0/.gitignore +51 -0
- ai_sdlc_framework-0.1.0/PKG-INFO +86 -0
- ai_sdlc_framework-0.1.0/README.md +58 -0
- ai_sdlc_framework-0.1.0/pyproject.toml +62 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/__init__.py +40 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/_utils/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/_utils/duration.py +42 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/__init__.py +1 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/event_bus.py +39 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/git_resolver.py +111 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/github.py +187 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/interfaces.py +353 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/linear.py +87 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/registry.py +115 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/resolve_secret.py +21 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/scanner.py +84 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/stubs/__init__.py +1 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/stubs/bitbucket.py +80 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/stubs/code_analysis.py +57 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/stubs/deployment_target.py +75 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/stubs/gitlab.py +135 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/stubs/jira.py +109 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/stubs/messenger.py +51 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/stubs/semgrep.py +73 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/stubs/sonarqube.py +61 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/adapters/webhook_bridge.py +62 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/agents/__init__.py +11 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/agents/discovery.py +161 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/agents/executor.py +350 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/agents/memory/__init__.py +1 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/agents/memory/file_backend.py +160 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/agents/memory/in_memory.py +171 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/agents/memory/types.py +86 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/agents/orchestration.py +101 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/agents/runner.py +166 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/agents/runner_registry.py +95 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/audit/__init__.py +1 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/audit/file_sink.py +101 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/audit/logger.py +131 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/audit/memory_sink.py +55 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/audit/types.py +60 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/builders/__init__.py +25 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/builders/builders.py +418 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/builders/distribution.py +95 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/compliance/__init__.py +1 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/compliance/checker.py +59 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/compliance/mappings.py +309 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/core/__init__.py +1 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/core/compare.py +38 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/core/provenance.py +112 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/core/types.py +548 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/core/validation.py +115 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/metrics/__init__.py +1 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/metrics/instrumentation.py +161 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/metrics/store.py +103 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/metrics/types.py +168 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/__init__.py +3 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/abac.py +95 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/admission.py +136 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/authentication.py +58 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/authorization.py +171 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/autonomy.py +145 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/cel_evaluator.py +335 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/complexity.py +142 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/enforcement.py +196 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/evaluator.py +156 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/expression.py +187 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/llm_evaluator.py +104 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/mutating_gate.py +98 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/policy/rego_evaluator.py +261 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/py.typed +0 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/reconciler/__init__.py +1 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/reconciler/autonomy_reconciler.py +98 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/reconciler/diff.py +71 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/reconciler/gate_reconciler.py +56 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/reconciler/loop.py +167 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/reconciler/pipeline_reconciler.py +234 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/reconciler/types.py +48 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/security/__init__.py +1 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/security/approval_tier.py +60 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/security/docker_sandbox.py +82 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/security/env_secret_store.py +44 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/security/github_jit.py +124 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/security/github_sandbox.py +104 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/security/interfaces.py +91 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/security/stubs.py +178 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/telemetry/__init__.py +1 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/telemetry/instrumentation.py +51 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/telemetry/logging.py +147 -0
- ai_sdlc_framework-0.1.0/src/ai_sdlc/telemetry/semantic_conventions.py +57 -0
- ai_sdlc_framework-0.1.0/tests/adapters/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/tests/adapters/test_event_bus.py +41 -0
- ai_sdlc_framework-0.1.0/tests/adapters/test_git_resolver.py +82 -0
- ai_sdlc_framework-0.1.0/tests/adapters/test_github_adapter.py +46 -0
- ai_sdlc_framework-0.1.0/tests/adapters/test_linear_adapter.py +20 -0
- ai_sdlc_framework-0.1.0/tests/adapters/test_registry.py +103 -0
- ai_sdlc_framework-0.1.0/tests/adapters/test_resolve_secret.py +21 -0
- ai_sdlc_framework-0.1.0/tests/adapters/test_scanner.py +75 -0
- ai_sdlc_framework-0.1.0/tests/adapters/test_stubs.py +181 -0
- ai_sdlc_framework-0.1.0/tests/adapters/test_webhook_bridge.py +47 -0
- ai_sdlc_framework-0.1.0/tests/agents/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/tests/agents/memory/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/tests/agents/memory/test_file_backend.py +96 -0
- ai_sdlc_framework-0.1.0/tests/agents/memory/test_in_memory.py +94 -0
- ai_sdlc_framework-0.1.0/tests/agents/test_discovery.py +145 -0
- ai_sdlc_framework-0.1.0/tests/agents/test_executor.py +260 -0
- ai_sdlc_framework-0.1.0/tests/agents/test_orchestration.py +90 -0
- ai_sdlc_framework-0.1.0/tests/agents/test_runner.py +149 -0
- ai_sdlc_framework-0.1.0/tests/agents/test_runner_registry.py +91 -0
- ai_sdlc_framework-0.1.0/tests/audit/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/tests/audit/test_logger.py +55 -0
- ai_sdlc_framework-0.1.0/tests/audit/test_memory_sink.py +16 -0
- ai_sdlc_framework-0.1.0/tests/builders/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/tests/builders/test_builders.py +141 -0
- ai_sdlc_framework-0.1.0/tests/builders/test_distribution.py +103 -0
- ai_sdlc_framework-0.1.0/tests/compliance/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/tests/compliance/test_checker.py +35 -0
- ai_sdlc_framework-0.1.0/tests/core/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/tests/core/test_compare.py +43 -0
- ai_sdlc_framework-0.1.0/tests/core/test_duration.py +47 -0
- ai_sdlc_framework-0.1.0/tests/core/test_provenance.py +85 -0
- ai_sdlc_framework-0.1.0/tests/core/test_types.py +142 -0
- ai_sdlc_framework-0.1.0/tests/core/test_validation.py +54 -0
- ai_sdlc_framework-0.1.0/tests/metrics/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/tests/metrics/test_instrumentation.py +125 -0
- ai_sdlc_framework-0.1.0/tests/metrics/test_store.py +74 -0
- ai_sdlc_framework-0.1.0/tests/policy/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_abac.py +90 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_admission.py +145 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_authentication.py +46 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_authorization.py +130 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_autonomy.py +201 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_cel_evaluator.py +106 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_complexity.py +61 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_enforcement.py +204 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_evaluator.py +210 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_expression.py +72 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_llm_evaluator.py +72 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_mutating_gate.py +65 -0
- ai_sdlc_framework-0.1.0/tests/policy/test_rego_evaluator.py +80 -0
- ai_sdlc_framework-0.1.0/tests/reconciler/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/tests/reconciler/test_autonomy_reconciler.py +166 -0
- ai_sdlc_framework-0.1.0/tests/reconciler/test_diff.py +88 -0
- ai_sdlc_framework-0.1.0/tests/reconciler/test_gate_reconciler.py +77 -0
- ai_sdlc_framework-0.1.0/tests/reconciler/test_loop.py +115 -0
- ai_sdlc_framework-0.1.0/tests/reconciler/test_pipeline_reconciler.py +167 -0
- ai_sdlc_framework-0.1.0/tests/reconciler/test_types.py +39 -0
- ai_sdlc_framework-0.1.0/tests/security/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/tests/security/test_approval_tier.py +47 -0
- ai_sdlc_framework-0.1.0/tests/security/test_docker_sandbox.py +51 -0
- ai_sdlc_framework-0.1.0/tests/security/test_env_secret_store.py +28 -0
- ai_sdlc_framework-0.1.0/tests/security/test_github_jit.py +72 -0
- ai_sdlc_framework-0.1.0/tests/security/test_github_sandbox.py +58 -0
- ai_sdlc_framework-0.1.0/tests/security/test_stubs.py +66 -0
- ai_sdlc_framework-0.1.0/tests/telemetry/__init__.py +0 -0
- ai_sdlc_framework-0.1.0/tests/telemetry/test_logging.py +39 -0
- ai_sdlc_framework-0.1.0/tests/telemetry/test_semantic_conventions.py +24 -0
- ai_sdlc_framework-0.1.0/tests/test_conformance.py +319 -0
- ai_sdlc_framework-0.1.0/tests/test_smoke.py +7 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Build output
|
|
5
|
+
dist/
|
|
6
|
+
*.tsbuildinfo
|
|
7
|
+
.next/
|
|
8
|
+
|
|
9
|
+
# Coverage
|
|
10
|
+
coverage/
|
|
11
|
+
.nyc_output/
|
|
12
|
+
|
|
13
|
+
# Environment
|
|
14
|
+
.env
|
|
15
|
+
.env.*
|
|
16
|
+
!.env.example
|
|
17
|
+
|
|
18
|
+
# IDE
|
|
19
|
+
.idea/
|
|
20
|
+
.vscode/
|
|
21
|
+
*.swp
|
|
22
|
+
*.swo
|
|
23
|
+
*~
|
|
24
|
+
|
|
25
|
+
# OS
|
|
26
|
+
.DS_Store
|
|
27
|
+
Thumbs.db
|
|
28
|
+
|
|
29
|
+
# Logs
|
|
30
|
+
*.log
|
|
31
|
+
npm-debug.log*
|
|
32
|
+
pnpm-debug.log*
|
|
33
|
+
|
|
34
|
+
# AI-SDLC runtime artifacts
|
|
35
|
+
.ai-sdlc/audit.jsonl
|
|
36
|
+
|
|
37
|
+
# Package manager
|
|
38
|
+
.pnpm-store/
|
|
39
|
+
|
|
40
|
+
# Python
|
|
41
|
+
__pycache__/
|
|
42
|
+
*.py[cod]
|
|
43
|
+
*.egg-info/
|
|
44
|
+
*.egg
|
|
45
|
+
*.whl
|
|
46
|
+
.venv/
|
|
47
|
+
.mypy_cache/
|
|
48
|
+
.pytest_cache/
|
|
49
|
+
.ruff_cache/
|
|
50
|
+
build/
|
|
51
|
+
*.pyz
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ai-sdlc-framework
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the AI-SDLC Framework
|
|
5
|
+
Project-URL: Homepage, https://github.com/ai-sdlc-framework/ai-sdlc
|
|
6
|
+
Project-URL: Repository, https://github.com/ai-sdlc-framework/ai-sdlc/tree/main/sdk-python
|
|
7
|
+
Project-URL: Issues, https://github.com/ai-sdlc-framework/ai-sdlc/issues
|
|
8
|
+
Author: AI-SDLC Framework
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
Keywords: agents,ai,pipelines,quality-gates,sdlc
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: jsonschema>=4.20
|
|
19
|
+
Requires-Dist: opentelemetry-api>=1.20
|
|
20
|
+
Requires-Dist: pydantic<3,>=2.0
|
|
21
|
+
Requires-Dist: pyyaml>=6.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# ai-sdlc
|
|
30
|
+
|
|
31
|
+
Python SDK for the [AI-SDLC Framework](https://ai-sdlc.io) — a Kubernetes-style declarative framework for governing AI agents in software development lifecycles.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install ai-sdlc
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from ai_sdlc.core.types import Pipeline, API_VERSION
|
|
43
|
+
from ai_sdlc.core.validation import validate_resource
|
|
44
|
+
from ai_sdlc.builders.builders import PipelineBuilder
|
|
45
|
+
|
|
46
|
+
# Build a pipeline using the fluent API
|
|
47
|
+
pipeline = (
|
|
48
|
+
PipelineBuilder("my-pipeline")
|
|
49
|
+
.add_trigger({"event": "issue.assigned"})
|
|
50
|
+
.add_provider("github", {"type": "github"})
|
|
51
|
+
.add_stage({"name": "implement"})
|
|
52
|
+
.build()
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Validate against JSON Schema
|
|
56
|
+
result = validate_resource(pipeline.model_dump(by_alias=True))
|
|
57
|
+
assert result.valid
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Modules
|
|
61
|
+
|
|
62
|
+
| Module | Description |
|
|
63
|
+
|--------|------------|
|
|
64
|
+
| `core` | Pydantic models for all 5 resource types, JSON Schema validation, comparison, provenance |
|
|
65
|
+
| `builders` | Fluent builder classes for resource construction |
|
|
66
|
+
| `policy` | Enforcement engine, autonomy evaluation, complexity routing, authorization |
|
|
67
|
+
| `adapters` | Interface Protocols, adapter registry, community stubs |
|
|
68
|
+
| `reconciler` | asyncio-based reconciliation loop with domain reconcilers |
|
|
69
|
+
| `agents` | Orchestration patterns, executor, multi-tier memory |
|
|
70
|
+
| `security` | Sandbox, JIT credentials, kill switch, approval workflow Protocols |
|
|
71
|
+
| `telemetry` | OpenTelemetry semantic conventions, structured logging |
|
|
72
|
+
| `compliance` | Regulatory framework mappings (EU AI Act, NIST AI RMF, ISO 42001, etc.) |
|
|
73
|
+
| `metrics` | Metric store, standard metric definitions |
|
|
74
|
+
| `audit` | JSONL audit logging with tamper-evident hashing |
|
|
75
|
+
|
|
76
|
+
## Requirements
|
|
77
|
+
|
|
78
|
+
- Python 3.11+
|
|
79
|
+
- pydantic >= 2.0
|
|
80
|
+
- jsonschema >= 4.20
|
|
81
|
+
- PyYAML >= 6.0
|
|
82
|
+
- opentelemetry-api >= 1.20
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
Apache-2.0
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# ai-sdlc
|
|
2
|
+
|
|
3
|
+
Python SDK for the [AI-SDLC Framework](https://ai-sdlc.io) — a Kubernetes-style declarative framework for governing AI agents in software development lifecycles.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install ai-sdlc
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from ai_sdlc.core.types import Pipeline, API_VERSION
|
|
15
|
+
from ai_sdlc.core.validation import validate_resource
|
|
16
|
+
from ai_sdlc.builders.builders import PipelineBuilder
|
|
17
|
+
|
|
18
|
+
# Build a pipeline using the fluent API
|
|
19
|
+
pipeline = (
|
|
20
|
+
PipelineBuilder("my-pipeline")
|
|
21
|
+
.add_trigger({"event": "issue.assigned"})
|
|
22
|
+
.add_provider("github", {"type": "github"})
|
|
23
|
+
.add_stage({"name": "implement"})
|
|
24
|
+
.build()
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Validate against JSON Schema
|
|
28
|
+
result = validate_resource(pipeline.model_dump(by_alias=True))
|
|
29
|
+
assert result.valid
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Modules
|
|
33
|
+
|
|
34
|
+
| Module | Description |
|
|
35
|
+
|--------|------------|
|
|
36
|
+
| `core` | Pydantic models for all 5 resource types, JSON Schema validation, comparison, provenance |
|
|
37
|
+
| `builders` | Fluent builder classes for resource construction |
|
|
38
|
+
| `policy` | Enforcement engine, autonomy evaluation, complexity routing, authorization |
|
|
39
|
+
| `adapters` | Interface Protocols, adapter registry, community stubs |
|
|
40
|
+
| `reconciler` | asyncio-based reconciliation loop with domain reconcilers |
|
|
41
|
+
| `agents` | Orchestration patterns, executor, multi-tier memory |
|
|
42
|
+
| `security` | Sandbox, JIT credentials, kill switch, approval workflow Protocols |
|
|
43
|
+
| `telemetry` | OpenTelemetry semantic conventions, structured logging |
|
|
44
|
+
| `compliance` | Regulatory framework mappings (EU AI Act, NIST AI RMF, ISO 42001, etc.) |
|
|
45
|
+
| `metrics` | Metric store, standard metric definitions |
|
|
46
|
+
| `audit` | JSONL audit logging with tamper-evident hashing |
|
|
47
|
+
|
|
48
|
+
## Requirements
|
|
49
|
+
|
|
50
|
+
- Python 3.11+
|
|
51
|
+
- pydantic >= 2.0
|
|
52
|
+
- jsonschema >= 4.20
|
|
53
|
+
- PyYAML >= 6.0
|
|
54
|
+
- opentelemetry-api >= 1.20
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
Apache-2.0
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ai-sdlc-framework"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for the AI-SDLC Framework"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "Apache-2.0"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "AI-SDLC Framework" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["ai", "sdlc", "agents", "quality-gates", "pipelines"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Typing :: Typed",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"pydantic>=2.0,<3",
|
|
26
|
+
"jsonschema>=4.20",
|
|
27
|
+
"PyYAML>=6.0",
|
|
28
|
+
"opentelemetry-api>=1.20",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/ai-sdlc-framework/ai-sdlc"
|
|
33
|
+
Repository = "https://github.com/ai-sdlc-framework/ai-sdlc/tree/main/sdk-python"
|
|
34
|
+
Issues = "https://github.com/ai-sdlc-framework/ai-sdlc/issues"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=7",
|
|
39
|
+
"pytest-asyncio>=0.21",
|
|
40
|
+
"mypy>=1.8",
|
|
41
|
+
"ruff>=0.4",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/ai_sdlc"]
|
|
46
|
+
|
|
47
|
+
[tool.pytest.ini_options]
|
|
48
|
+
testpaths = ["tests"]
|
|
49
|
+
asyncio_mode = "auto"
|
|
50
|
+
|
|
51
|
+
[tool.mypy]
|
|
52
|
+
strict = true
|
|
53
|
+
python_version = "3.11"
|
|
54
|
+
plugins = ["pydantic.mypy"]
|
|
55
|
+
|
|
56
|
+
[tool.ruff]
|
|
57
|
+
target-version = "py311"
|
|
58
|
+
line-length = 99
|
|
59
|
+
|
|
60
|
+
[tool.ruff.lint]
|
|
61
|
+
select = ["E", "F", "I", "N", "W", "UP", "B", "SIM", "TC"]
|
|
62
|
+
ignore = ["SIM118"]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""AI-SDLC Python SDK — native implementation of the AI-SDLC Framework."""
|
|
2
|
+
|
|
3
|
+
__version__ = "0.1.0"
|
|
4
|
+
|
|
5
|
+
from ai_sdlc.builders.builders import (
|
|
6
|
+
AdapterBindingBuilder,
|
|
7
|
+
AgentRoleBuilder,
|
|
8
|
+
AutonomyPolicyBuilder,
|
|
9
|
+
PipelineBuilder,
|
|
10
|
+
QualityGateBuilder,
|
|
11
|
+
)
|
|
12
|
+
from ai_sdlc.core.types import (
|
|
13
|
+
API_VERSION,
|
|
14
|
+
AdapterBinding,
|
|
15
|
+
AgentRole,
|
|
16
|
+
AnyResource,
|
|
17
|
+
AutonomyPolicy,
|
|
18
|
+
Pipeline,
|
|
19
|
+
QualityGate,
|
|
20
|
+
ResourceKind,
|
|
21
|
+
)
|
|
22
|
+
from ai_sdlc.core.validation import validate_resource
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"__version__",
|
|
26
|
+
"API_VERSION",
|
|
27
|
+
"AdapterBinding",
|
|
28
|
+
"AgentRole",
|
|
29
|
+
"AnyResource",
|
|
30
|
+
"AutonomyPolicy",
|
|
31
|
+
"Pipeline",
|
|
32
|
+
"QualityGate",
|
|
33
|
+
"ResourceKind",
|
|
34
|
+
"validate_resource",
|
|
35
|
+
"AdapterBindingBuilder",
|
|
36
|
+
"AgentRoleBuilder",
|
|
37
|
+
"AutonomyPolicyBuilder",
|
|
38
|
+
"PipelineBuilder",
|
|
39
|
+
"QualityGateBuilder",
|
|
40
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Parse duration strings in shorthand (60s, 5m, 2h, 1d, 2w) or ISO 8601 format."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
_SHORTHAND_RE = re.compile(r"^(\d+)([smhdw])$")
|
|
8
|
+
_ISO_RE = re.compile(r"^PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$")
|
|
9
|
+
|
|
10
|
+
_SHORTHAND_MULTIPLIERS: dict[str, int] = {
|
|
11
|
+
"s": 1_000,
|
|
12
|
+
"m": 60_000,
|
|
13
|
+
"h": 3_600_000,
|
|
14
|
+
"d": 86_400_000,
|
|
15
|
+
"w": 604_800_000,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def parse_duration(value: str) -> int:
|
|
20
|
+
"""Parse a duration string and return milliseconds.
|
|
21
|
+
|
|
22
|
+
Supports:
|
|
23
|
+
- Shorthand: ``60s``, ``5m``, ``2h``, ``1d``, ``2w``
|
|
24
|
+
- ISO 8601: ``PT1H30M``, ``PT30S``
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
ValueError: If the duration string is invalid.
|
|
28
|
+
"""
|
|
29
|
+
m = _SHORTHAND_RE.match(value)
|
|
30
|
+
if m:
|
|
31
|
+
return int(m.group(1)) * _SHORTHAND_MULTIPLIERS[m.group(2)]
|
|
32
|
+
|
|
33
|
+
m = _ISO_RE.match(value)
|
|
34
|
+
if m:
|
|
35
|
+
hours = int(m.group(1) or 0)
|
|
36
|
+
minutes = int(m.group(2) or 0)
|
|
37
|
+
seconds = int(m.group(3) or 0)
|
|
38
|
+
if hours == 0 and minutes == 0 and seconds == 0:
|
|
39
|
+
raise ValueError(f"Invalid ISO 8601 duration: {value}")
|
|
40
|
+
return (hours * 3600 + minutes * 60 + seconds) * 1000
|
|
41
|
+
|
|
42
|
+
raise ValueError(f"Invalid duration: {value}")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Adapter interfaces, registry, and stubs."""
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""In-process EventBus implementation backed by a simple dict of handlers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InProcessEventBus:
|
|
12
|
+
"""In-process pub/sub event bus."""
|
|
13
|
+
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
self._handlers: dict[str, list[Callable[..., Any]]] = {}
|
|
16
|
+
|
|
17
|
+
async def publish(self, topic: str, payload: Any) -> None:
|
|
18
|
+
for handler in self._handlers.get(topic, []):
|
|
19
|
+
handler(payload)
|
|
20
|
+
|
|
21
|
+
def subscribe(
|
|
22
|
+
self, topic: str, handler: Callable[..., Any],
|
|
23
|
+
) -> Callable[[], None]:
|
|
24
|
+
self._handlers.setdefault(topic, []).append(handler)
|
|
25
|
+
|
|
26
|
+
def unsubscribe() -> None:
|
|
27
|
+
handlers = self._handlers.get(topic, [])
|
|
28
|
+
if handler in handlers:
|
|
29
|
+
handlers.remove(handler)
|
|
30
|
+
|
|
31
|
+
return unsubscribe
|
|
32
|
+
|
|
33
|
+
def subscriber_count(self, topic: str) -> int:
|
|
34
|
+
return len(self._handlers.get(topic, []))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def create_in_process_event_bus() -> InProcessEventBus:
|
|
38
|
+
"""Create an in-process event bus."""
|
|
39
|
+
return InProcessEventBus()
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Git-based adapter resolver.
|
|
2
|
+
|
|
3
|
+
Resolves adapter metadata from git repository references (PRD Section 9.3).
|
|
4
|
+
Format: ``github.com/<org>/<repo>@<ref>``
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Protocol
|
|
12
|
+
|
|
13
|
+
from ai_sdlc.adapters.registry import AdapterMetadata, validate_adapter_metadata
|
|
14
|
+
from ai_sdlc.adapters.scanner import parse_metadata_yaml
|
|
15
|
+
|
|
16
|
+
_GIT_REF_PATTERN = re.compile(r"^([^/]+)/([^/]+)/([^@]+)@(.+)$")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class GitAdapterReference:
|
|
21
|
+
host: str
|
|
22
|
+
org: str
|
|
23
|
+
repo: str
|
|
24
|
+
ref: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class GitAdapterFetcher(Protocol):
|
|
28
|
+
"""Abstraction for fetching raw file content from a git host."""
|
|
29
|
+
|
|
30
|
+
async def fetch(self, url: str) -> str | None: ...
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class GitResolveResult:
|
|
35
|
+
metadata: AdapterMetadata | None
|
|
36
|
+
error: str | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def parse_git_adapter_ref(ref: str) -> GitAdapterReference:
|
|
40
|
+
"""Parse a git adapter reference string.
|
|
41
|
+
|
|
42
|
+
Expected format: ``github.com/org/repo@v1.0.0``
|
|
43
|
+
"""
|
|
44
|
+
match = _GIT_REF_PATTERN.match(ref)
|
|
45
|
+
if not match:
|
|
46
|
+
raise ValueError(
|
|
47
|
+
f'Invalid git adapter reference "{ref}": '
|
|
48
|
+
"expected format <host>/<org>/<repo>@<ref>"
|
|
49
|
+
)
|
|
50
|
+
return GitAdapterReference(
|
|
51
|
+
host=match.group(1),
|
|
52
|
+
org=match.group(2),
|
|
53
|
+
repo=match.group(3),
|
|
54
|
+
ref=match.group(4),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def build_raw_url(parsed: GitAdapterReference) -> str:
|
|
59
|
+
"""Build the raw content URL for a metadata.yaml file."""
|
|
60
|
+
if parsed.host == "github.com":
|
|
61
|
+
return (
|
|
62
|
+
f"https://raw.githubusercontent.com/"
|
|
63
|
+
f"{parsed.org}/{parsed.repo}/{parsed.ref}/metadata.yaml"
|
|
64
|
+
)
|
|
65
|
+
raise ValueError(f"Unsupported git host: {parsed.host}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def create_stub_git_adapter_fetcher(entries: dict[str, str]) -> GitAdapterFetcher:
|
|
69
|
+
"""Create a stub fetcher for testing. Maps URLs to YAML content strings."""
|
|
70
|
+
|
|
71
|
+
class _StubFetcher:
|
|
72
|
+
async def fetch(self, url: str) -> str | None:
|
|
73
|
+
return entries.get(url)
|
|
74
|
+
|
|
75
|
+
return _StubFetcher()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
async def resolve_git_adapter(
|
|
79
|
+
ref: str,
|
|
80
|
+
fetcher: GitAdapterFetcher,
|
|
81
|
+
) -> GitResolveResult:
|
|
82
|
+
"""Resolve adapter metadata from a git reference."""
|
|
83
|
+
try:
|
|
84
|
+
parsed = parse_git_adapter_ref(ref)
|
|
85
|
+
except ValueError as err:
|
|
86
|
+
return GitResolveResult(metadata=None, error=str(err))
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
url = build_raw_url(parsed)
|
|
90
|
+
except ValueError as err:
|
|
91
|
+
return GitResolveResult(metadata=None, error=str(err))
|
|
92
|
+
|
|
93
|
+
content = await fetcher.fetch(url)
|
|
94
|
+
if content is None:
|
|
95
|
+
return GitResolveResult(
|
|
96
|
+
metadata=None, error=f"Failed to fetch metadata from {url}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
metadata = parse_metadata_yaml(content)
|
|
101
|
+
except Exception as err:
|
|
102
|
+
return GitResolveResult(metadata=None, error=f"Invalid YAML: {err}")
|
|
103
|
+
|
|
104
|
+
validation = validate_adapter_metadata(metadata)
|
|
105
|
+
if not validation.valid:
|
|
106
|
+
return GitResolveResult(
|
|
107
|
+
metadata=None,
|
|
108
|
+
error=f"Validation failed: {'; '.join(validation.errors)}",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return GitResolveResult(metadata=metadata)
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""GitHub adapter stubs — SourceControl, IssueTracker, CIPipeline.
|
|
2
|
+
|
|
3
|
+
These are stub/protocol implementations. Real API calls require
|
|
4
|
+
an injected Octokit-like client (not bundled as a dependency).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Protocol
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from collections.abc import AsyncIterator
|
|
14
|
+
|
|
15
|
+
from ai_sdlc.adapters.interfaces import (
|
|
16
|
+
Branch,
|
|
17
|
+
Build,
|
|
18
|
+
BuildEvent,
|
|
19
|
+
BuildFilter,
|
|
20
|
+
BuildStatus,
|
|
21
|
+
ChangedFile,
|
|
22
|
+
CommitStatus,
|
|
23
|
+
CoverageReport,
|
|
24
|
+
CreateBranchInput,
|
|
25
|
+
CreateIssueInput,
|
|
26
|
+
CreatePRInput,
|
|
27
|
+
FileContent,
|
|
28
|
+
Issue,
|
|
29
|
+
IssueComment,
|
|
30
|
+
IssueEvent,
|
|
31
|
+
IssueFilter,
|
|
32
|
+
MergeResult,
|
|
33
|
+
MergeStrategy,
|
|
34
|
+
PREvent,
|
|
35
|
+
PRFilter,
|
|
36
|
+
PullRequest,
|
|
37
|
+
TestResults,
|
|
38
|
+
TriggerBuildInput,
|
|
39
|
+
UpdateIssueInput,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class GitHubConfig:
|
|
45
|
+
org: str
|
|
46
|
+
repo: str | None = None
|
|
47
|
+
token_secret_ref: str | None = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class GitHubClient(Protocol):
|
|
51
|
+
"""Minimal protocol for a GitHub API client."""
|
|
52
|
+
|
|
53
|
+
async def request(
|
|
54
|
+
self, method: str, url: str, **kwargs: Any,
|
|
55
|
+
) -> Any: ...
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _empty_async_iter() -> AsyncIterator[Any]:
|
|
59
|
+
async def _gen() -> AsyncIterator[Any]:
|
|
60
|
+
return
|
|
61
|
+
yield # noqa: RET504 # pragma: no cover
|
|
62
|
+
return _gen()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class _GitHubIssueTracker:
|
|
66
|
+
def __init__(self, client: GitHubClient, config: GitHubConfig) -> None:
|
|
67
|
+
self._client = client
|
|
68
|
+
self._config = config
|
|
69
|
+
|
|
70
|
+
async def list_issues(self, filter: IssueFilter) -> list[Issue]:
|
|
71
|
+
return []
|
|
72
|
+
|
|
73
|
+
async def get_issue(self, id: str) -> Issue:
|
|
74
|
+
return Issue(
|
|
75
|
+
id=id, title="", status="open",
|
|
76
|
+
url=f"https://github.com/{self._config.org}/{self._config.repo}/issues/{id}",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
async def create_issue(self, input: CreateIssueInput) -> Issue:
|
|
80
|
+
return Issue(
|
|
81
|
+
id="1", title=input.title, status="open",
|
|
82
|
+
url=f"https://github.com/{self._config.org}/{self._config.repo}/issues/1",
|
|
83
|
+
description=input.description, labels=input.labels,
|
|
84
|
+
assignee=input.assignee,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
async def update_issue(self, id: str, input: UpdateIssueInput) -> Issue:
|
|
88
|
+
return await self.get_issue(id)
|
|
89
|
+
|
|
90
|
+
async def transition_issue(self, id: str, transition: str) -> Issue:
|
|
91
|
+
return await self.get_issue(id)
|
|
92
|
+
|
|
93
|
+
async def add_comment(self, id: str, body: str) -> None:
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
async def get_comments(self, id: str) -> list[IssueComment]:
|
|
97
|
+
return []
|
|
98
|
+
|
|
99
|
+
def watch_issues(self, filter: IssueFilter) -> AsyncIterator[IssueEvent]:
|
|
100
|
+
return _empty_async_iter()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class _GitHubSourceControl:
|
|
104
|
+
def __init__(self, client: GitHubClient, config: GitHubConfig) -> None:
|
|
105
|
+
self._client = client
|
|
106
|
+
self._config = config
|
|
107
|
+
|
|
108
|
+
async def create_branch(self, input: CreateBranchInput) -> Branch:
|
|
109
|
+
return Branch(name=input.name, sha="stub-sha")
|
|
110
|
+
|
|
111
|
+
async def create_pr(self, input: CreatePRInput) -> PullRequest:
|
|
112
|
+
return PullRequest(
|
|
113
|
+
id="1", title=input.title,
|
|
114
|
+
source_branch=input.source_branch,
|
|
115
|
+
target_branch=input.target_branch,
|
|
116
|
+
status="open", author="stub-user",
|
|
117
|
+
url=f"https://github.com/{self._config.org}/{self._config.repo}/pull/1",
|
|
118
|
+
description=input.description,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
async def merge_pr(self, id: str, strategy: MergeStrategy) -> MergeResult:
|
|
122
|
+
return MergeResult(sha="merge-sha", merged=True)
|
|
123
|
+
|
|
124
|
+
async def get_file_contents(self, path: str, ref: str) -> FileContent:
|
|
125
|
+
return FileContent(path=path, content="", encoding="utf-8")
|
|
126
|
+
|
|
127
|
+
async def list_changed_files(self, pr_id: str) -> list[ChangedFile]:
|
|
128
|
+
return []
|
|
129
|
+
|
|
130
|
+
async def set_commit_status(
|
|
131
|
+
self, sha: str, status: CommitStatus,
|
|
132
|
+
) -> None:
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
def watch_pr_events(self, filter: PRFilter) -> AsyncIterator[PREvent]:
|
|
136
|
+
return _empty_async_iter()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class _GitHubCIPipeline:
|
|
140
|
+
def __init__(self, client: GitHubClient, config: GitHubConfig) -> None:
|
|
141
|
+
self._client = client
|
|
142
|
+
self._config = config
|
|
143
|
+
|
|
144
|
+
async def trigger_build(self, input: TriggerBuildInput) -> Build:
|
|
145
|
+
return Build(id="1", status="queued")
|
|
146
|
+
|
|
147
|
+
async def get_build_status(self, id: str) -> BuildStatus:
|
|
148
|
+
return BuildStatus(id=id, status="succeeded")
|
|
149
|
+
|
|
150
|
+
async def get_test_results(self, build_id: str) -> TestResults:
|
|
151
|
+
return TestResults(passed=0, failed=0, skipped=0)
|
|
152
|
+
|
|
153
|
+
async def get_coverage_report(self, build_id: str) -> CoverageReport:
|
|
154
|
+
return CoverageReport(line_coverage=0.0)
|
|
155
|
+
|
|
156
|
+
def watch_build_events(
|
|
157
|
+
self, filter: BuildFilter,
|
|
158
|
+
) -> AsyncIterator[BuildEvent]:
|
|
159
|
+
return _empty_async_iter()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def create_github_issue_tracker(
|
|
163
|
+
config: GitHubConfig, client: GitHubClient | None = None,
|
|
164
|
+
) -> _GitHubIssueTracker:
|
|
165
|
+
"""Create a GitHub IssueTracker adapter."""
|
|
166
|
+
return _GitHubIssueTracker(client or _NoOpClient(), config)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def create_github_source_control(
|
|
170
|
+
config: GitHubConfig, client: GitHubClient | None = None,
|
|
171
|
+
) -> _GitHubSourceControl:
|
|
172
|
+
"""Create a GitHub SourceControl adapter."""
|
|
173
|
+
return _GitHubSourceControl(client or _NoOpClient(), config)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def create_github_ci_pipeline(
|
|
177
|
+
config: GitHubConfig, client: GitHubClient | None = None,
|
|
178
|
+
) -> _GitHubCIPipeline:
|
|
179
|
+
"""Create a GitHub CIPipeline adapter."""
|
|
180
|
+
return _GitHubCIPipeline(client or _NoOpClient(), config)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class _NoOpClient:
|
|
184
|
+
async def request(
|
|
185
|
+
self, method: str, url: str, **kwargs: Any,
|
|
186
|
+
) -> Any:
|
|
187
|
+
return None
|