feat: add gitea agentic runtime control plane

This commit is contained in:
2026-03-13 15:34:18 +08:00
parent 6f6acdb0e6
commit ae540c7890
58 changed files with 1851 additions and 1 deletions

84
tests/unit/test_cli.py Normal file
View File

@@ -0,0 +1,84 @@
from __future__ import annotations
import json
from pathlib import Path
from engine.devops_agent import cli
class FakeProvider:
def __init__(self, *, base_url: str, token: str) -> None:
self.base_url = base_url
self.token = token
def parse_issue_comment_event(self, payload: dict[str, object]) -> dict[str, object]:
repository = payload["repository"]
issue = payload["issue"]
comment = payload["comment"]
return {
"repo": repository["full_name"],
"issue_number": issue["number"],
"comment_body": comment["body"],
}
def get_issue(self, repo: str, issue_number: int) -> dict[str, object]:
return {
"number": issue_number,
"title": "Fix issue delivery flow",
"body": "The agent should post evidence back to the issue.",
"state": "open",
"repo": repo,
}
def post_issue_comment(self, repo: str, issue_number: int, body: str) -> dict[str, object]:
return {"id": 1, "repo": repo, "issue_number": issue_number, "body": body}
def test_compile_command_writes_lock_file() -> None:
output_path = Path(".tmp/cli-tests/gitea-issue-delivery.lock.json")
output_path.parent.mkdir(parents=True, exist_ok=True)
exit_code = cli.main(
[
"compile",
"workflows/gitea-issue-delivery.md",
"--output",
str(output_path),
]
)
assert exit_code == 0
assert output_path.exists()
def test_validate_command_returns_success() -> None:
exit_code = cli.main(["validate", "workflows/gitea-issue-delivery.md"])
assert exit_code == 0
def test_run_command_writes_runtime_artifact(monkeypatch) -> None:
output_dir = Path(".tmp/cli-tests/runtime-run")
output_dir.mkdir(parents=True, exist_ok=True)
monkeypatch.setattr(cli, "GiteaProvider", FakeProvider)
exit_code = cli.main(
[
"run",
"workflows/gitea-issue-delivery.md",
"--event-payload",
"tests/fixtures/gitea/comment_event.json",
"--output-dir",
str(output_dir),
"--base-url",
"https://fun-md.com",
"--token",
"fake-token",
]
)
artifact_path = output_dir / "run-artifact.json"
artifact = json.loads(artifact_path.read_text(encoding="utf-8"))
assert exit_code == 0
assert artifact["result"] == "success"

View File

@@ -0,0 +1,26 @@
from __future__ import annotations
from pathlib import Path
from engine.devops_agent.compiler import compile_workflow
from engine.devops_agent.spec import load_workflow_spec
def test_compile_emits_normalized_lock_payload() -> None:
spec = load_workflow_spec(Path("workflows/gitea-issue-delivery.md"))
lock = compile_workflow(spec)
assert lock["version"] == 1
assert lock["workflow_name"] == "gitea-issue-delivery"
assert lock["provider"] == "gitea"
assert lock["triggers"] == [
{
"event": "issue_comment",
"commands": ["@devops-agent"],
}
]
assert lock["policy"]["path_scope"] == []
assert lock["policy"]["require_human_merge"] is True
assert lock["safe_outputs"]["add_comment"]["max"] == 3
assert "issue_comment" in lock["required_evidence"]

View File

@@ -0,0 +1,72 @@
from __future__ import annotations
import json
from pathlib import Path
from engine.devops_agent.providers.gitea import GiteaProvider
class FakeTransport:
def __init__(self) -> None:
self.calls: list[dict[str, object]] = []
def __call__(
self,
*,
method: str,
url: str,
headers: dict[str, str],
body: dict[str, object] | None,
) -> dict[str, object]:
self.calls.append(
{
"method": method,
"url": url,
"headers": headers,
"body": body,
}
)
if url.endswith("/comments"):
return {"id": 999, "body": body["body"] if body else ""}
return json.loads(Path("tests/fixtures/gitea/issue.json").read_text(encoding="utf-8"))
def test_gitea_provider_fetches_issue() -> None:
transport = FakeTransport()
provider = GiteaProvider(
base_url="https://fun-md.com",
token="test-token",
transport=transport,
)
issue = provider.get_issue("Fun_MD/devops-skills", 48)
assert issue["number"] == 48
assert transport.calls[0]["method"] == "GET"
assert str(transport.calls[0]["url"]).endswith("/api/v1/repos/Fun_MD/devops-skills/issues/48")
def test_gitea_provider_posts_issue_comment() -> None:
transport = FakeTransport()
provider = GiteaProvider(
base_url="https://fun-md.com",
token="test-token",
transport=transport,
)
response = provider.post_issue_comment("Fun_MD/devops-skills", 48, "Evidence posted")
assert response["id"] == 999
assert transport.calls[0]["method"] == "POST"
assert transport.calls[0]["body"] == {"body": "Evidence posted"}
def test_gitea_provider_parses_comment_event() -> None:
provider = GiteaProvider(base_url="https://fun-md.com", token="test-token")
payload = json.loads(Path("tests/fixtures/gitea/comment_event.json").read_text(encoding="utf-8"))
event = provider.parse_issue_comment_event(payload)
assert event["repo"] == "Fun_MD/devops-skills"
assert event["issue_number"] == 48
assert "@devops-agent" in event["comment_body"]

View File

@@ -0,0 +1,34 @@
from __future__ import annotations
import pytest
from engine.devops_agent.policies import PolicyViolation, RuntimePolicy
def test_policy_allows_declared_safe_output() -> None:
policy = RuntimePolicy(
safe_outputs={"add_comment": {"max": 2}},
path_scope=["engine/devops_agent/", "README.md"],
)
policy.assert_operation_allowed("add_comment")
def test_policy_rejects_undeclared_write_action() -> None:
policy = RuntimePolicy(
safe_outputs={"add_comment": {"max": 2}},
path_scope=[],
)
with pytest.raises(PolicyViolation, match="close_issue"):
policy.assert_operation_allowed("close_issue")
def test_policy_rejects_paths_outside_scope() -> None:
policy = RuntimePolicy(
safe_outputs={"write_file": {"max": 5}},
path_scope=["engine/devops_agent/"],
)
with pytest.raises(PolicyViolation, match="outside allowed path scope"):
policy.assert_path_allowed("skills/gitea-issue-devops-agent/SKILL.md")

View File

@@ -0,0 +1,11 @@
import importlib
def test_core_modules_are_importable() -> None:
module_names = [
"engine.devops_agent",
"engine.devops_agent.cli",
]
for module_name in module_names:
importlib.import_module(module_name)

View File

@@ -0,0 +1,29 @@
from __future__ import annotations
from pathlib import Path
import pytest
from engine.devops_agent.spec import WorkflowSpecError, load_workflow_spec
def test_load_workflow_spec_splits_frontmatter_and_body() -> None:
spec = load_workflow_spec(Path("tests/fixtures/specs/valid_workflow.md"))
assert spec.name == "issue-delivery"
assert spec.provider == "gitea"
assert spec.frontmatter["safe_outputs"]["add_comment"]["max"] == 2
assert "Read the selected issue" in spec.body
def test_load_workflow_spec_rejects_missing_provider() -> None:
with pytest.raises(WorkflowSpecError, match="provider"):
load_workflow_spec(Path("tests/fixtures/specs/invalid_missing_provider.md"))
def test_sample_workflow_spec_exists_and_loads() -> None:
spec = load_workflow_spec(Path("workflows/gitea-issue-delivery.md"))
assert spec.name == "gitea-issue-delivery"
assert spec.provider == "gitea"
assert "add_comment" in spec.frontmatter["safe_outputs"]

View File

@@ -0,0 +1,30 @@
from __future__ import annotations
from pathlib import Path
from engine.devops_agent.spec import load_workflow_spec
from engine.devops_agent.validator import validate_workflow_spec
def test_validate_accepts_the_sample_workflow() -> None:
spec = load_workflow_spec(Path("workflows/gitea-issue-delivery.md"))
errors = validate_workflow_spec(spec)
assert errors == []
def test_validate_rejects_write_permissions_without_safe_outputs() -> None:
spec = load_workflow_spec(Path("tests/fixtures/specs/no_safe_outputs_for_write.md"))
errors = validate_workflow_spec(spec)
assert any("safe_outputs" in error for error in errors)
def test_validate_rejects_invalid_path_scope() -> None:
spec = load_workflow_spec(Path("tests/fixtures/specs/invalid_path_scope.md"))
errors = validate_workflow_spec(spec)
assert any("path_scope" in error for error in errors)