feat: complete PR creation via GiteaClient in commit_and_open_pr and open_pr

This commit is contained in:
머니페니 2026-03-20 17:44:45 +09:00
parent af7bd2cdc3
commit 816415dd24
3 changed files with 181 additions and 5 deletions

View File

@ -16,6 +16,7 @@ from langchain.agents.middleware import AgentState, after_agent
from langgraph.config import get_config
from langgraph.runtime import Runtime
from ..utils.gitea_client import get_gitea_client
from ..utils.git_utils import (
git_add_all,
git_checkout_branch,
@ -135,8 +136,21 @@ async def open_pr_if_needed(
git_push, sandbox_backend, repo_dir, target_branch, gitea_token
)
# TODO: Phase 2 - use GiteaClient to create PR via Gitea API
logger.info("Pushed to branch %s, PR creation pending Gitea integration", target_branch)
# --- PR 생성 (GiteaClient) ---
default_branch = os.environ.get("DEFAULT_BRANCH", "main")
client = get_gitea_client()
try:
pr_result = await client.create_pull_request(
owner=repo_owner,
repo=repo_name,
title=pr_title,
head=target_branch,
base=default_branch,
body=pr_body,
)
logger.info("Safety net PR created: %s", pr_result.get("html_url"))
except Exception:
logger.exception("Safety net PR creation failed (changes were pushed)")
logger.info("After-agent middleware completed successfully")

View File

@ -1,9 +1,12 @@
import asyncio
import logging
import os
from typing import Any
from langgraph.config import get_config
from agent.utils.gitea_client import get_gitea_client
from ..utils.git_utils import (
git_add_all,
git_checkout_branch,
@ -95,7 +98,6 @@ def commit_and_open_pr(
"pr_url": None,
}
import os
gitea_token = os.environ.get("GITEA_TOKEN", "")
if not gitea_token:
logger.error("commit_and_open_pr missing Gitea token for thread %s", thread_id)
@ -113,8 +115,39 @@ def commit_and_open_pr(
"pr_url": None,
}
# TODO: Phase 2 - use GiteaClient to create PR
return {"success": True, "pr_url": "pending-gitea-implementation"}
# --- PR 생성 (GiteaClient) ---
gitea_external_url = os.environ.get("GITEA_EXTERNAL_URL", "")
gitea_internal_url = os.environ.get("GITEA_URL", "http://gitea:3000")
default_branch = os.environ.get("DEFAULT_BRANCH", "main")
client = get_gitea_client()
try:
pr_result = asyncio.run(
client.create_pull_request(
owner=repo_owner,
repo=repo_name,
title=title,
head=target_branch,
base=default_branch,
body=body,
)
)
pr_url = pr_result.get("html_url", "")
if gitea_external_url and pr_url:
pr_url = pr_url.replace(gitea_internal_url, gitea_external_url)
return {
"success": True,
"pr_url": pr_url,
"pr_number": pr_result.get("number"),
}
except Exception as e:
logger.exception("Failed to create PR (push succeeded)")
return {
"success": True,
"pr_url": "",
"error": f"Push succeeded but PR creation failed: {e}",
}
except Exception as e:
logger.exception("commit_and_open_pr failed")

View File

@ -0,0 +1,129 @@
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
def test_pr_creation_after_push():
"""push 성공 후 GiteaClient로 PR을 생성한다."""
mock_gitea = MagicMock()
mock_gitea.create_pull_request = AsyncMock(
return_value={
"number": 1,
"html_url": "http://gitea:3000/quant/galaxis-po/pulls/1",
}
)
mock_sandbox = MagicMock()
mock_result = MagicMock(exit_code=0, output="")
with patch(
"agent.tools.commit_and_open_pr.get_gitea_client", return_value=mock_gitea
), patch(
"agent.tools.commit_and_open_pr.get_sandbox_backend_sync",
return_value=mock_sandbox,
), patch(
"agent.tools.commit_and_open_pr.get_config",
return_value={
"configurable": {
"thread_id": "test-thread",
"repo": {"owner": "quant", "name": "galaxis-po"},
}
},
), patch(
"agent.tools.commit_and_open_pr.resolve_repo_dir",
return_value="/workspace/galaxis-po",
), patch(
"agent.tools.commit_and_open_pr.git_has_uncommitted_changes",
return_value=True,
), patch(
"agent.tools.commit_and_open_pr.git_fetch_origin",
), patch(
"agent.tools.commit_and_open_pr.git_has_unpushed_commits",
return_value=False,
), patch(
"agent.tools.commit_and_open_pr.git_current_branch",
return_value="galaxis-agent/test-thread",
), patch(
"agent.tools.commit_and_open_pr.git_checkout_branch",
), patch(
"agent.tools.commit_and_open_pr.git_config_user",
), patch(
"agent.tools.commit_and_open_pr.git_add_all",
), patch(
"agent.tools.commit_and_open_pr.git_commit",
return_value=mock_result,
), patch(
"agent.tools.commit_and_open_pr.git_push",
return_value=mock_result,
), patch.dict(
"os.environ", {"GITEA_TOKEN": "test-token"},
):
from agent.tools.commit_and_open_pr import commit_and_open_pr
result = commit_and_open_pr(title="feat: add feature", body="PR description")
assert result["success"] is True
assert "pulls/1" in result["pr_url"]
mock_gitea.create_pull_request.assert_called_once()
def test_pr_creation_converts_internal_to_external_url():
"""PR URL이 내부 URL에서 외부 URL로 변환된다."""
mock_gitea = MagicMock()
mock_gitea.create_pull_request = AsyncMock(
return_value={
"number": 5,
"html_url": "http://gitea:3000/quant/galaxis-po/pulls/5",
}
)
mock_sandbox = MagicMock()
mock_result = MagicMock(exit_code=0, output="")
with patch(
"agent.tools.commit_and_open_pr.get_gitea_client", return_value=mock_gitea
), patch(
"agent.tools.commit_and_open_pr.get_sandbox_backend_sync",
return_value=mock_sandbox,
), patch(
"agent.tools.commit_and_open_pr.get_config",
return_value={
"configurable": {
"thread_id": "test-thread",
"repo": {"owner": "quant", "name": "galaxis-po"},
}
},
), patch(
"agent.tools.commit_and_open_pr.resolve_repo_dir",
return_value="/workspace/galaxis-po",
), patch(
"agent.tools.commit_and_open_pr.git_has_uncommitted_changes",
return_value=True,
), patch(
"agent.tools.commit_and_open_pr.git_fetch_origin",
), patch(
"agent.tools.commit_and_open_pr.git_has_unpushed_commits",
return_value=False,
), patch(
"agent.tools.commit_and_open_pr.git_current_branch",
return_value="galaxis-agent/test-thread",
), patch(
"agent.tools.commit_and_open_pr.git_checkout_branch",
), patch(
"agent.tools.commit_and_open_pr.git_config_user",
), patch(
"agent.tools.commit_and_open_pr.git_add_all",
), patch(
"agent.tools.commit_and_open_pr.git_commit",
return_value=mock_result,
), patch(
"agent.tools.commit_and_open_pr.git_push",
return_value=mock_result,
), patch.dict(
"os.environ",
{
"GITEA_TOKEN": "test-token",
"GITEA_EXTERNAL_URL": "https://ayuriel.duckdns.org",
"GITEA_URL": "http://gitea:3000",
},
):
from agent.tools.commit_and_open_pr import commit_and_open_pr
result = commit_and_open_pr(title="feat: test", body="body")
assert result["success"] is True
assert "ayuriel.duckdns.org" in result["pr_url"]
assert "gitea:3000" not in result["pr_url"]