Skip to content

Commit 338f5c7

Browse files
0xba1aCopilot
andauthored
Update CopilotBot to use compatible versions of SDK and CLI (#149)
* Update copilotBot to be compatible with latest broken releases of Copilot-SDK and CLI * Pin copilot-cli and SDK to a compatible versions * Update copilot tests * Fix version pinning config line * Update byok config for CopilotBot testing * Fix env variable names * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Update Test Summary * Update openai api compatible url for byok * Update copilotBot byok provider env to just "azure" * Update wire_api and api_version env variable * Fix env variable naming * Run unit tests prior to all other tests * Bring back the unit tests to the matrix but set the copilot env variables only for ghcp tests * Set env variable for a particular test case --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 11f8ba3 commit 338f5c7

5 files changed

Lines changed: 107 additions & 31 deletions

File tree

.github/workflows/test.yml

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
matrix:
1616
# Installing ollama model in GitHub Actions runner requires significant disk space.
1717
# It reduces the space available for browser-based tests
18-
test-type: ["unit", "integration", "ollama_local", "slow-browser", "slow-other"]
18+
test-type: ["unit", "integration", "ollama_local", "slow-browser", "slow-other", "ghcp"]
1919
include:
2020
- test-type: "unit"
2121
pytest-args: "-m 'unit and not ollama_local'"
@@ -27,6 +27,8 @@ jobs:
2727
pytest-args: "-m 'slow' test/bot/test_browsing_bot.py"
2828
- test-type: "slow-other"
2929
pytest-args: "-m 'slow' --ignore=test/bot/test_browsing_bot.py"
30+
- test-type: "ghcp"
31+
pytest-args: "-m 'ghcp'"
3032

3133

3234
steps:
@@ -87,6 +89,11 @@ jobs:
8789
run: |
8890
pip install -e .
8991
92+
- name: Install GitHub Copilot SDK dependencies for GHCP tests
93+
if: matrix.test-type == 'ghcp'
94+
run: |
95+
pip install "github-copilot-sdk==0.3.0"
96+
9097
- name: Build Docker images for integration tests
9198
if: matrix.test-type != 'unit'
9299
run: |
@@ -138,6 +145,15 @@ jobs:
138145
#Local Model Configuration
139146
LOCAL_MODEL_NAME: "qwen2.5-coder:latest"
140147
LOCAL_MODEL_PORT: 11434
148+
# CopilotBot Configuration (only populated for ghcp tests)
149+
COPILOT_BYOK_API_KEY: ${{ matrix.test-type == 'ghcp' && secrets.AZURE_OPENAI_API_KEY || '' }}
150+
COPILOT_BYOK_BASE_URL: ${{ matrix.test-type == 'ghcp' && vars.OPENAI_ENDPOINT || '' }}
151+
COPILOT_BYOK_PROVIDER_TYPE: ${{ matrix.test-type == 'ghcp' && 'azure' || '' }}
152+
COPILOT_BYOK_MODEL: ${{ matrix.test-type == 'ghcp' && vars.AZURE_OPENAI_DEPLOYMENT_NAME || '' }}
153+
COPILOT_BYOK_API_VERSION: ${{ matrix.test-type == 'ghcp' && vars.AZURE_OPENAI_API_VERSION || '' }}
154+
COPILOT_BYOK_WIRE_API: ${{ matrix.test-type == 'ghcp' && 'openai' || '' }}
155+
# Passing BYOK as arguement. Test: TestCopilotBotBYOKOpenAIIntegration::test_byok_openai_simple_task
156+
OPENAI_ENDPOINT: ${{ matrix.test-type == 'ghcp' && vars.OPENAI_ENDPOINT || '' }}
141157
run: |
142158
python -m pytest ${{ matrix.pytest-args }} \
143159
-n auto \
@@ -195,28 +211,30 @@ jobs:
195211
echo "## Test Results Summary" >> $GITHUB_STEP_SUMMARY
196212
echo "| Test Type | Status |" >> $GITHUB_STEP_SUMMARY
197213
echo "|-----------|--------|" >> $GITHUB_STEP_SUMMARY
198-
214+
199215
# Check each test result file and parse for failures
200-
for test_type in unit integration ollama_local slow-browser slow-other; do
216+
for test_type in unit integration ollama_local slow-browser slow-other ghcp; do
201217
if [ -f "test-results-${test_type}.xml" ]; then
202218
failures=$(grep -oP 'failures="\K[0-9]+' "test-results-${test_type}.xml" | head -1)
203219
errors=$(grep -oP 'errors="\K[0-9]+' "test-results-${test_type}.xml" | head -1)
204-
220+
205221
if [ "${failures:-0}" -eq 0 ] && [ "${errors:-0}" -eq 0 ]; then
206222
status="✅ Passed"
207223
else
208224
status="❌ Failed"
209225
fi
210-
226+
211227
# Format test type name nicely
212228
case $test_type in
213229
unit) name="Unit Tests" ;;
214230
integration) name="Integration Tests" ;;
215231
ollama_local) name="Ollama Tests" ;;
216232
slow-browser) name="Slow Browser Tests" ;;
217233
slow-other) name="Slow Other Tests" ;;
234+
ghcp) name="GitHub Copilot Tests" ;;
235+
*) name="$test_type" ;;
218236
esac
219-
237+
220238
echo "| ${name} | ${status} |" >> $GITHUB_STEP_SUMMARY
221239
fi
222240
done

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ classifiers = [
2222
requires-python = ">=3.11"
2323

2424
[project.optional-dependencies]
25-
ghcp = ["github-copilot-sdk"]
25+
ghcp = ["github-copilot-sdk==0.3.0"]
2626
azure_ad = ["azure-identity>=1.15.0"]
2727

2828
[tool.setuptools.dynamic]

pytest.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
testpaths = test
33
python_files = test_*.py
44
python_functions = test_*
5-
addopts =
5+
addopts =
66
-v
77
--tb=short
88
--strict-markers
@@ -13,3 +13,4 @@ markers =
1313
integration: Integration tests
1414
slow: Slow tests
1515
docker: marks tests that require a running Docker daemon and pull container images
16+
ghcp: marks GitHub Copilot related tests

src/microbots/bot/CopilotBot.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
_BYOK_ENV_BASE_URL = "COPILOT_BYOK_BASE_URL"
7272
_BYOK_ENV_API_KEY = "COPILOT_BYOK_API_KEY"
7373
_BYOK_ENV_BEARER_TOKEN = "COPILOT_BYOK_BEARER_TOKEN"
74-
_BYOK_ENV_WIRE_API = "COPILOT_BYOK_WIRE_API"
74+
_BYOK_ENV_WIRE_API = "COPILOT_BYOK_WIRE_API" # openai / responses
7575
_BYOK_ENV_AZURE_API_VERSION = "COPILOT_BYOK_AZURE_API_VERSION"
7676
_BYOK_ENV_MODEL = "COPILOT_BYOK_MODEL"
7777

@@ -380,6 +380,7 @@ def __init__(
380380

381381
# ── Connect SDK to in-container CLI ─────────────────────────
382382
container_ip = self.environment.get_ipv4_address()
383+
logger.info("🔌 Connecting Copilot SDK to CLI server at %s:%d", container_ip, _CONTAINER_CLI_PORT)
383384
self._client = CopilotClient(
384385
ExternalServerConfig(url=f"{container_ip}:{_CONTAINER_CLI_PORT}")
385386
)
@@ -514,7 +515,8 @@ def _install_copilot_cli(self):
514515
"curl -fsSL https://deb.nodesource.com/setup_22.x | bash - > /dev/null 2>&1",
515516
"apt-get install -y -qq nodejs > /dev/null 2>&1",
516517
# Install copilot-cli globally
517-
"npm install -g @github/copilot > /dev/null 2>&1",
518+
# NOTE: Pinning to 1.0.39 which is the latest @github/copilot release compatible with github-copilot-sdk==0.3.0 as of May 2026.
519+
"npm install -g @github/copilot@1.0.39 > /dev/null 2>&1",
518520
]
519521

520522
for cmd in install_commands:
@@ -555,7 +557,7 @@ def _start_copilot_cli_server(self):
555557
# Start copilot in headless mode in the background
556558
# Using nohup + & to run it as a background process inside the container's shell
557559
start_cmd = (
558-
f"nohup copilot --headless --port {_CONTAINER_CLI_PORT} "
560+
f"nohup copilot --headless --port {_CONTAINER_CLI_PORT} --host 0.0.0.0 "
559561
f"> /var/log/copilot-cli.log 2>&1 &"
560562
)
561563
result = self.environment.execute(start_cmd)
@@ -579,13 +581,15 @@ def _wait_for_cli_ready(self):
579581
deadline = time.time() + _CLI_STARTUP_TIMEOUT
580582
while time.time() < deadline:
581583
try:
584+
self.environment.execute("pgrep -f 'copilot.*--headless' || true")
582585
sock = _socket.create_connection(
583-
(container_ip, _CONTAINER_CLI_PORT), timeout=2
586+
(container_ip, _CONTAINER_CLI_PORT), timeout=5
584587
)
585588
sock.close()
586589
return
587590
except (ConnectionRefusedError, OSError):
588591
time.sleep(1)
592+
self.environment.execute("cat /var/log/copilot-cli.log || true")
589593
raise TimeoutError(
590594
f"copilot-cli did not become ready within {_CLI_STARTUP_TIMEOUT}s "
591595
f"on {container_ip}:{_CONTAINER_CLI_PORT}"

test/bot/test_copilot_bot.py

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,6 @@ def _swap_copilot_sdk_layout(*, session_has_handler: bool, types_has_handler: bo
147147
# Helpers
148148
# ---------------------------------------------------------------------------
149149

150-
def _copilot_cli_available():
151-
return shutil.which("copilot") is not None
152-
153-
154150
def _copilot_sdk_installed():
155151
try:
156152
from importlib.metadata import version
@@ -1599,14 +1595,9 @@ def test_run_with_old_sdk_layout_types(self):
15991595

16001596

16011597
# ---------------------------------------------------------------------------
1602-
# Integration tests — require real Docker + copilot-cli + auth
1598+
# Integration tests — require real Docker + copilot-SDK + auth
16031599
# ---------------------------------------------------------------------------
16041600

1605-
_skip_no_copilot_cli = pytest.mark.skipif(
1606-
not _copilot_cli_available(),
1607-
reason="GitHub Copilot CLI not installed (copilot not in PATH)",
1608-
)
1609-
16101601
_skip_no_copilot_sdk = pytest.mark.skipif(
16111602
not _copilot_sdk_installed(),
16121603
reason="github-copilot-sdk not installed (pip install microbots[ghcp])",
@@ -1618,11 +1609,10 @@ def test_run_with_old_sdk_layout_types(self):
16181609
)
16191610

16201611

1621-
@_skip_no_copilot_cli
16221612
@_skip_no_copilot_sdk
1623-
@_skip_no_copilot_auth
16241613
@pytest.mark.integration
16251614
@pytest.mark.slow
1615+
@pytest.mark.ghcp
16261616
class TestCopilotBotIntegration:
16271617
"""End-to-end integration tests with real Copilot SDK."""
16281618

@@ -1631,7 +1621,7 @@ def test_simple_task(self, test_repo, issue_1):
16311621
_restore_real_copilot_modules()
16321622
from microbots.bot.CopilotBot import CopilotBot
16331623

1634-
issue_text = issue_1[0]
1624+
issue_text = issue_1[0] + "\nFix the error in the original file."
16351625
verify_function = issue_1[1]
16361626

16371627
bot = CopilotBot(
@@ -1658,22 +1648,38 @@ def test_simple_task(self, test_repo, issue_1):
16581648
def _byok_openai_available():
16591649
"""Check if OpenAI BYOK credentials are configured via env vars."""
16601650
return bool(
1661-
os.environ.get("OPEN_AI_KEY")
1662-
and os.environ.get("OPEN_AI_END_POINT")
1651+
os.environ.get("AZURE_OPENAI_API_KEY")
1652+
and os.environ.get("AZURE_OPENAI_ENDPOINT")
16631653
)
16641654

16651655

16661656
_skip_no_byok_openai = pytest.mark.skipif(
16671657
not _byok_openai_available(),
1668-
reason="OpenAI BYOK not configured (set OPEN_AI_KEY and OPEN_AI_END_POINT)",
1658+
reason="OpenAI BYOK not configured (set env variables AZURE_OPENAI_API_KEY and AZURE_OPENAI_ENDPOINT)",
1659+
)
1660+
1661+
def _azure_ad_auth_available():
1662+
"""Check if Azure AD auth is available via DefaultAzureCredential."""
1663+
try:
1664+
from azure.identity import DefaultAzureCredential
1665+
credential = DefaultAzureCredential()
1666+
# Attempt to get a token to verify credentials are working
1667+
credential.get_token("https://cognitiveservices.azure.com/.default")
1668+
return True
1669+
except Exception:
1670+
return False
1671+
1672+
_skip_no_azure_ad_auth = pytest.mark.skipif(
1673+
not _azure_ad_auth_available(),
1674+
reason="Azure AD auth not available (set up DefaultAzureCredential)",
16691675
)
16701676

16711677

1672-
@_skip_no_copilot_cli
16731678
@_skip_no_copilot_sdk
16741679
@_skip_no_byok_openai
16751680
@pytest.mark.integration
16761681
@pytest.mark.slow
1682+
@pytest.mark.ghcp
16771683
class TestCopilotBotBYOKOpenAIIntegration:
16781684
"""End-to-end integration tests for CopilotBot with OpenAI BYOK."""
16791685

@@ -1682,22 +1688,68 @@ def test_byok_openai_simple_task(self, test_repo, issue_1):
16821688
_restore_real_copilot_modules()
16831689
from microbots.bot.CopilotBot import CopilotBot
16841690

1691+
issue_text = issue_1[0] + "\nFix the error in the original file."
1692+
verify_function = issue_1[1]
1693+
1694+
api_key = os.environ["AZURE_OPENAI_API_KEY"]
1695+
base_url = os.environ["OPENAI_ENDPOINT"]
1696+
model = os.getenv(
1697+
"AZURE_OPENAI_DEPLOYMENT_NAME", "mini-swe-agent-gpt5"
1698+
)
1699+
1700+
bot = CopilotBot(
1701+
model=model,
1702+
folder_to_mount=str(test_repo),
1703+
permission="READ_WRITE",
1704+
api_key=api_key,
1705+
base_url=base_url,
1706+
provider_type="openai",
1707+
)
1708+
1709+
try:
1710+
assert bot._provider_config is not None
1711+
assert bot._provider_config["type"] == "openai"
1712+
assert bot.github_token is None
1713+
1714+
result = bot.run(
1715+
issue_text,
1716+
timeout_in_seconds=300,
1717+
)
1718+
assert result.status is True, f"CopilotBot BYOK run failed: {result.error}"
1719+
verify_function(test_repo)
1720+
finally:
1721+
bot.stop()
1722+
1723+
@_skip_no_azure_ad_auth
1724+
def test_byok_openai_simple_task_with_token_provider(self, test_repo, issue_1):
1725+
"""CopilotBot can fix a simple syntax error using OpenAI BYOK credentials."""
1726+
_restore_real_copilot_modules()
1727+
from microbots.bot.CopilotBot import CopilotBot
1728+
16851729
issue_text = issue_1[0]
16861730
verify_function = issue_1[1]
16871731

1688-
api_key = os.environ["OPEN_AI_KEY"]
1689-
base_url = os.environ["OPEN_AI_END_POINT"]
1732+
api_key = os.environ["AZURE_OPENAI_API_KEY"]
1733+
base_url = os.environ["AZURE_OPENAI_ENDPOINT"]
16901734
model = os.getenv(
16911735
"AZURE_OPENAI_DEPLOYMENT_NAME", "mini-swe-agent-gpt5"
16921736
)
16931737

1738+
from azure.identity import DefaultAzureCredential
1739+
credential = DefaultAzureCredential()
1740+
def get_token():
1741+
return credential.get_token(
1742+
"https://cognitiveservices.azure.com/.default"
1743+
).token
1744+
16941745
bot = CopilotBot(
16951746
model=model,
16961747
folder_to_mount=str(test_repo),
16971748
permission="READ_WRITE",
16981749
api_key=api_key,
16991750
base_url=base_url,
17001751
provider_type="openai",
1752+
token_provider=get_token,
17011753
)
17021754

17031755
try:
@@ -1713,3 +1765,4 @@ def test_byok_openai_simple_task(self, test_repo, issue_1):
17131765
verify_function(test_repo)
17141766
finally:
17151767
bot.stop()
1768+

0 commit comments

Comments
 (0)