Skip to content

Commit bfccb2e

Browse files
Copilotnotfolder
andcommitted
Add bot assignment functionality for GitHub/GitLab issues and PRs
- Add GITHUB_BOT_NAME and GITLAB_BOT_NAME environment variables - Update frameworks to assign issues and PRs to specified bots - Add assign_pull_request() and assign_merge_request() methods - Update configuration files to use bot name environment variables - Update documentation and examples with bot assignment setup - Enhance check_config.py to show bot assignment status - All ruff lint errors resolved Co-authored-by: notfolder <20558197+notfolder@users.noreply.github.com>
1 parent 5c1095c commit bfccb2e

9 files changed

Lines changed: 202 additions & 10 deletions

tests/real_integration/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# Required scopes: repo
99
GITHUB_PERSONAL_ACCESS_TOKEN=ghp_your_github_token_here
1010
GITHUB_TEST_REPO=yourusername/your-test-repository
11+
GITHUB_BOT_NAME=coding-agent-bot
1112

1213
# ================================
1314
# GitLab Configuration (Optional)
@@ -17,6 +18,7 @@ GITHUB_TEST_REPO=yourusername/your-test-repository
1718
GITLAB_PERSONAL_ACCESS_TOKEN=glpat-your_gitlab_token_here
1819
GITLAB_TEST_PROJECT=your-project-id-or-path
1920
GITLAB_API_URL=https://gitlab.com/api/v4
21+
GITLAB_BOT_NAME=coding-agent-bot
2022

2123
# ================================
2224
# LLM Configuration (Required)

tests/real_integration/QUICKSTART.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This is a quick guide to get the real integration tests running.
1414
```bash
1515
export GITHUB_PERSONAL_ACCESS_TOKEN="ghp_your_token_here"
1616
export GITHUB_TEST_REPO="myuser/coding-agent-test"
17+
export GITHUB_BOT_NAME="coding-agent-bot" # Optional: bot for assignment
1718
export OPENAI_API_KEY="sk_your_openai_key_here"
1819
# Optional: For local LLM via OpenAI-compatible API
1920
export OPENAI_BASE_URL="http://localhost:1234/v1"
@@ -85,6 +86,7 @@ In your test repository:
8586
```bash
8687
export GITLAB_PERSONAL_ACCESS_TOKEN="glpat_your_token_here"
8788
export GITLAB_TEST_PROJECT="123" # or "myuser/project"
89+
export GITLAB_BOT_NAME="coding-agent-bot" # Optional: bot for assignment
8890
export OPENAI_API_KEY="sk_your_openai_key_here"
8991
```
9092

tests/real_integration/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,13 @@ Create a `.env` file or set environment variables:
6161
# For GitHub testing
6262
export GITHUB_PERSONAL_ACCESS_TOKEN="ghp_your_token_here"
6363
export GITHUB_TEST_REPO="yourusername/your-test-repo"
64+
export GITHUB_BOT_NAME="coding-agent-bot" # Optional: bot username for assignment
6465

6566
# For GitLab testing
6667
export GITLAB_PERSONAL_ACCESS_TOKEN="glpat-your_token_here"
6768
export GITLAB_TEST_PROJECT="123" # or "yourusername/your-test-project"
6869
export GITLAB_API_URL="https://gitlab.com/api/v4"
70+
export GITLAB_BOT_NAME="coding-agent-bot" # Optional: bot username for assignment
6971

7072
# LLM configuration
7173
export LLM_PROVIDER="openai"

tests/real_integration/base_framework.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ def __init__(self, platform: str = "github") -> None:
4848
# Load configuration
4949
self.config = self._load_config()
5050

51+
# Get bot name from environment variables (optional)
52+
self.bot_name = self._get_bot_name()
53+
5154
def _check_prerequisites(self) -> None:
5255
"""必要な環境変数が設定されているかをチェックする."""
5356
if self.platform == "github":
@@ -105,6 +108,19 @@ def _load_config(self) -> dict[str, Any]:
105108

106109
return config
107110

111+
def _get_bot_name(self) -> str | None:
112+
"""プラットフォーム固有のボット名を環境変数から取得する.
113+
114+
Returns:
115+
ボット名、または設定されていない場合はNone
116+
117+
"""
118+
if self.platform == "github":
119+
return os.environ.get("GITHUB_BOT_NAME")
120+
if self.platform == "gitlab":
121+
return os.environ.get("GITLAB_BOT_NAME")
122+
return None
123+
108124
def setup_test_environment(self) -> None:
109125
"""テスト環境をセットアップする."""
110126
self.logger.info("Setting up test environment for %s", self.platform)
@@ -179,7 +195,7 @@ def create_issue(
179195
labels = [self.config[self.platform]["bot_label"]]
180196

181197
# これは各プラットフォームで実装されます
182-
issue_data = self._create_issue_impl(title, body, labels)
198+
issue_data = self._create_issue_impl(title, body, labels, self.bot_name)
183199

184200
# クリーンアップタスクを追加
185201
def cleanup() -> None:
@@ -192,8 +208,25 @@ def cleanup() -> None:
192208

193209
return issue_data
194210

195-
def _create_issue_impl(self, title: str, body: str, labels: list[str]) -> dict[str, Any]:
196-
"""プラットフォーム固有のイシュー作成実装."""
211+
def _create_issue_impl(
212+
self,
213+
title: str,
214+
body: str,
215+
labels: list[str],
216+
assignee: str | None = None,
217+
) -> dict[str, Any]:
218+
"""プラットフォーム固有のイシュー作成実装.
219+
220+
Args:
221+
title: イシューのタイトル
222+
body: イシューの本文
223+
labels: ラベルのリスト
224+
assignee: アサインするユーザー名(オプション)
225+
226+
Returns:
227+
作成されたイシューの詳細を含む辞書
228+
229+
"""
197230
msg = "サブクラスは_create_issue_implを実装する必要があります"
198231
raise NotImplementedError(msg)
199232

tests/real_integration/check_config.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,24 @@ def main() -> None:
157157

158158
if github_configured:
159159
checks.append(("GitHub", lambda: True))
160+
print("✅ GitHub testing enabled") # noqa: T201
161+
github_bot = os.environ.get("GITHUB_BOT_NAME")
162+
if github_bot:
163+
print(f" Bot assignment: {github_bot}") # noqa: T201
164+
else:
165+
print(" Bot assignment: Not configured (issues won't be assigned)") # noqa: T201
166+
160167
if gitlab_configured:
161168
checks.append(("GitLab", lambda: True))
169+
print("✅ GitLab testing enabled") # noqa: T201
170+
gitlab_bot = os.environ.get("GITLAB_BOT_NAME")
171+
if gitlab_bot:
172+
print(f" Bot assignment: {gitlab_bot}") # noqa: T201
173+
else:
174+
print(" Bot assignment: Not configured (issues won't be assigned)") # noqa: T201
162175

163176
if not github_configured and not gitlab_configured:
177+
print("❌ No platform configured. Please set up GitHub or GitLab credentials.") # noqa: T201
164178
sys.exit(1)
165179

166180
# Run remaining checks
@@ -170,8 +184,10 @@ def main() -> None:
170184
all_passed = False
171185

172186
if all_passed and (github_configured or gitlab_configured):
173-
pass
187+
print("\n🎉 All configuration checks passed! Ready to run real integration tests.") # noqa: T201
188+
print("Run: python tests/run_tests.py --real") # noqa: T201
174189
else:
190+
print("\n❌ Some configuration checks failed. Please review your setup.") # noqa: T201
175191
sys.exit(1)
176192

177193

tests/real_integration/github_framework.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,25 @@ def __init__(self) -> None:
3434
"Accept": "application/vnd.github.v3+json",
3535
}
3636

37-
def _create_issue_impl(self, title: str, body: str, labels: list[str]) -> dict[str, Any]:
38-
"""GitHubイシューを作成する."""
37+
def _create_issue_impl(
38+
self,
39+
title: str,
40+
body: str,
41+
labels: list[str],
42+
assignee: str | None = None,
43+
) -> dict[str, Any]:
44+
"""GitHubイシューを作成する.
45+
46+
Args:
47+
title: イシューのタイトル
48+
body: イシューの本文
49+
labels: ラベルのリスト
50+
assignee: アサインするユーザー名(オプション)
51+
52+
Returns:
53+
作成されたイシューの詳細を含む辞書
54+
55+
"""
3956
owner, repo = self.test_repo.split("/")
4057

4158
url = f"{self.api_base}/repos/{owner}/{repo}/issues"
@@ -45,12 +62,19 @@ def _create_issue_impl(self, title: str, body: str, labels: list[str]) -> dict[s
4562
"labels": labels,
4663
}
4764

65+
# アサイニーが指定されている場合は追加
66+
if assignee:
67+
data["assignees"] = [assignee]
68+
4869
response = requests.post(url, json=data, headers=self.headers, timeout=REQUEST_TIMEOUT)
4970
response.raise_for_status()
5071

5172
issue_data = response.json()
5273
self.logger.info("Created GitHub issue #%s: %s", issue_data["number"], title)
5374

75+
if assignee:
76+
self.logger.info("Assigned issue #%s to %s", issue_data["number"], assignee)
77+
5478
return issue_data
5579

5680
def _close_issue(self, issue_number: int) -> None:
@@ -153,6 +177,35 @@ def get_latest_pull_request(
153177
pull_requests = response.json()
154178
return pull_requests[0] if pull_requests else None
155179

180+
def assign_pull_request(self, pr_number: int, assignee: str) -> bool:
181+
"""GitHubプルリクエストにユーザーをアサインする.
182+
183+
Args:
184+
pr_number: プルリクエスト番号
185+
assignee: アサインするユーザー名
186+
187+
Returns:
188+
成功した場合True、失敗した場合False
189+
190+
"""
191+
if not assignee:
192+
return False
193+
194+
owner, repo = self.test_repo.split("/")
195+
196+
url = f"{self.api_base}/repos/{owner}/{repo}/issues/{pr_number}/assignees"
197+
data = {"assignees": [assignee]}
198+
199+
try:
200+
response = requests.post(url, json=data, headers=self.headers, timeout=REQUEST_TIMEOUT)
201+
response.raise_for_status()
202+
self.logger.info("Assigned PR #%s to %s", pr_number, assignee)
203+
except requests.RequestException as e:
204+
self.logger.warning("Failed to assign PR #%s to %s: %s", pr_number, assignee, e)
205+
return False
206+
207+
return True
208+
156209
def _create_label_if_not_exists(self, label: str) -> None:
157210
"""GitHubリポジトリでラベルが存在しない場合は作成する."""
158211
owner, repo = self.test_repo.split("/")

tests/real_integration/gitlab_framework.py

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,47 @@ def __init__(self) -> None:
3131
"Content-Type": "application/json",
3232
}
3333

34-
def _create_issue_impl(self, title: str, body: str, labels: list[str]) -> dict[str, Any]:
35-
"""Create a GitLab issue."""
34+
def _create_issue_impl(
35+
self,
36+
title: str,
37+
body: str,
38+
labels: list[str],
39+
assignee: str | None = None,
40+
) -> dict[str, Any]:
41+
"""GitLabイシューを作成する.
42+
43+
Args:
44+
title: イシューのタイトル
45+
body: イシューの本文
46+
labels: ラベルのリスト
47+
assignee: アサインするユーザー名(オプション)
48+
49+
Returns:
50+
作成されたイシューの詳細を含む辞書
51+
52+
"""
3653
url = f"{self.api_base}/projects/{self.test_project_id}/issues"
3754
data = {
3855
"title": title,
3956
"description": body,
4057
"labels": ",".join(labels),
4158
}
4259

60+
# アサイニーが指定されている場合はユーザーIDを取得して追加
61+
if assignee:
62+
assignee_id = self._get_user_id(assignee)
63+
if assignee_id:
64+
data["assignee_id"] = assignee_id
65+
4366
response = requests.post(url, json=data, headers=self.headers, timeout=REQUEST_TIMEOUT)
4467
response.raise_for_status()
4568

4669
issue_data = response.json()
4770
self.logger.info("Created GitLab issue #%s: %s", issue_data["iid"], title)
4871

72+
if assignee and assignee_id:
73+
self.logger.info("Assigned issue #%s to %s", issue_data["iid"], assignee)
74+
4975
return issue_data
5076

5177
def _close_issue(self, issue_iid: int) -> None:
@@ -135,6 +161,64 @@ def get_latest_merge_request(self, source_branch: str | None = None) -> dict[str
135161
merge_requests = response.json()
136162
return merge_requests[0] if merge_requests else None
137163

164+
def _get_user_id(self, username: str) -> int | None:
165+
"""GitLabユーザー名からユーザーIDを取得する.
166+
167+
Args:
168+
username: GitLabユーザー名
169+
170+
Returns:
171+
ユーザーID、または見つからない場合はNone
172+
173+
"""
174+
url = f"{self.api_base}/users"
175+
params = {"username": username}
176+
177+
try:
178+
response = requests.get(
179+
url, params=params, headers=self.headers, timeout=REQUEST_TIMEOUT,
180+
)
181+
response.raise_for_status()
182+
users = response.json()
183+
184+
if users:
185+
return users[0]["id"]
186+
except requests.RequestException as e:
187+
self.logger.warning("Failed to get user ID for %s: %s", username, e)
188+
189+
return None
190+
191+
def assign_merge_request(self, mr_iid: int, assignee: str) -> bool:
192+
"""GitLabマージリクエストにユーザーをアサインする.
193+
194+
Args:
195+
mr_iid: マージリクエストのIID
196+
assignee: アサインするユーザー名
197+
198+
Returns:
199+
成功した場合True、失敗した場合False
200+
201+
"""
202+
if not assignee:
203+
return False
204+
205+
assignee_id = self._get_user_id(assignee)
206+
if not assignee_id:
207+
return False
208+
209+
url = f"{self.api_base}/projects/{self.test_project_id}/merge_requests/{mr_iid}"
210+
data = {"assignee_id": assignee_id}
211+
212+
try:
213+
response = requests.put(url, json=data, headers=self.headers, timeout=REQUEST_TIMEOUT)
214+
response.raise_for_status()
215+
self.logger.info("Assigned MR #%s to %s", mr_iid, assignee)
216+
except requests.RequestException as e:
217+
self.logger.warning("Failed to assign MR #%s to %s: %s", mr_iid, assignee, e)
218+
return False
219+
220+
return True
221+
138222
def _create_label_if_not_exists(self, label: str) -> None:
139223
"""Create a label if it doesn't exist in GitLab project."""
140224
# Get existing labels

tests/real_test_config_github.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ github:
2424
processing_label: "coding agent processing"
2525
done_label: "coding agent done"
2626
query: 'state:open label:"coding agent"'
27-
assignee: "coding-agent-bot"
27+
assignee: "${GITHUB_BOT_NAME:-coding-agent-bot}"
2828

2929
max_llm_process_num: 10
3030
use_rabbitmq: false

tests/real_test_config_gitlab.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ gitlab:
2525
processing_label: "coding agent processing"
2626
done_label: "coding agent done"
2727
query: 'state=opened&labels=coding agent'
28-
assignee: "coding-agent-bot"
28+
assignee: "${GITLAB_BOT_NAME:-coding-agent-bot}"
2929

3030
max_llm_process_num: 10
3131
use_rabbitmq: false

0 commit comments

Comments
 (0)