{"meta":{"title":"诊断 CI 测试失败","intro":"用于 Copilot CLI 拉取 CI 日志、将故障关联到本地代码，并在不离开终端的情况下修复问题。","product":"GitHub Copilot","breadcrumbs":[{"href":"/zh/copilot","title":"GitHub Copilot"},{"href":"/zh/copilot/tutorials","title":"教程"},{"href":"/zh/copilot/tutorials/copilot-cookbook","title":"GitHub Copilot 指南"},{"href":"/zh/copilot/tutorials/copilot-cookbook/debug-errors","title":"调试中出现的错误"},{"href":"/zh/copilot/tutorials/copilot-cookbook/debug-errors/diagnose-ci-test-failures","title":"排查 CI 测试失败问题"}],"documentType":"article"},"body":"# 诊断 CI 测试失败\n\n用于 Copilot CLI 拉取 CI 日志、将故障关联到本地代码，并在不离开终端的情况下修复问题。\n\nCopilot CLI 内置 GitHub MCP 服务器，使其能够直接访问你的 GitHub Actions 工作流运行记录、作业日志和检查状态。 结合对本地文件的访问权限，它可以提取 CI 故障详细信息，将它们与代码相关联，并从终端提出修补程序。\n\n## 示例方案 1：测试在本地通过，但在 CI 中失败\n\n你有一个测试用例，它在你的本地机器上可以通过，但在 CI 中却失败了。 你可以要求 Copilot CLI 直接调查此测试失败。\n\n在此示例中，所测试的代码定义了一个简单的订单服务（`order.py`），并且有一个相应的测试来检查订单是否是今天创建的（`test_order_service.py`）。\n\n### 示例提示\n\n```copilot copy\nMy CI is failing on this branch. Can you pull the latest workflow run \nlogs, figure out what is failing, and help me fix it? The relevant files \nare @order.py and @test_order_service.py\n```\n\n### 示例响应\n\n> \\[!NOTE] 以下响应是示例。 Copilot Chat 的回答是不确定的，因此你可能会得到与这里所显示的不同的回答。\n\nCopilot CLI使用 GitHub MCP 服务器提取当前分支上的最新工作流运行、标识失败的作业并检索其日志。 它发现以下故障：\n\n```text\n___ TestOrderService.test_order_created_today ___\n>       assert order[\"created_date\"] == date.today()\nE       AssertionError: assert datetime.date(2024, 1, 15) == datetime.date(2024, 1, 16)\n\ntest_order_service.py:45: AssertionError\n```\n\n读取完这两个本地文件后，Copilot CLI注意到日期恰好相差一天，并认定这是**时区边界问题**。 CI 运行程序使用 UTC，而本地计算机使用不同的时区。 测试在午夜附近运行时， `date.today()` 在每个环境中返回不同的日期。\n\nCopilot CLI 建议通过注入时钟依赖项或固定时区来标准化 `today` 计算方式：\n\n```python\nfrom datetime import timezone, datetime\n\ndef get_today():\n    return datetime.now(timezone.utc).date()\n```\n\n它将该修复应用于 `order.py` 和 `test_order_service.py` 两者，并将所有引用更新为使用新的 `get_today()` 辅助函数。\n\n修复后，可以验证更改并在本地运行测试：\n\n1. 运行 `/diff` 以准确查看 Copilot CLI 更改了什么。\n2. 运行 `!pytest test_order_service.py` 以确认测试在本地通过。\n3. 推送修补程序，让 CI 重新运行。\n\n## 示例场景 2：存在竞态条件的间歇性测试失败\n\n测试有时通过，有时在同一台计算机上失败。 你需要 Copilot CLI 比较成功运行和失败运行中的日志，以找出原因。\n\n在此示例中，受测代码使用 `order_service.py` 中的后台作业异步更新订单状态，并在 `test_order_service.py` 中测试断言最终状态为 `\"processed\"`。\n\n### 竞态条件示例提示\n\n```copilot copy\nThis test passes sometimes and fails sometimes. Can you pull the logs \nfrom the last passing and failing CI runs for test_order_processed \non this branch, compare them, and figure out what is going wrong? \nRelevant files: @order_service.py @test_order_service.py\n```\n\n### 竞争条件的响应示例\n\n> \\[!NOTE] 以下响应是示例。 Copilot Chat 的回答是不确定的，因此你可能会得到与这里所显示的不同的回答。\n\nCopilot CLI使用 GitHub MCP 服务器列出当前分支上最近的工作流运行，识别出一个成功的运行和一个失败的运行，并获取这两个运行的日志。 它会比较它们：\n\n**传递运行：**\n\n```text\n[DEBUG] Created order #1234\n[DEBUG] Background job started for order #1234\n[DEBUG] Background job completed (52ms)\n[DEBUG] Checking order status\n[DEBUG] Order #1234 status: processed\n```\n\n**失败运行：**\n\n```text\n[DEBUG] Created order #1234\n[DEBUG] Background job started for order #1234\n[DEBUG] Checking order status\n[DEBUG] Order #1234 status: pending\n```\n\nCopilot CLI 发现，在通过的那次运行中，后台作业在状态检查之前已完成；而在失败的那次运行中，检查状态时作业仍在运行。 它将其识别为**竞争条件**，因为测试没有等待后台作业完成。\n\nCopilot CLI 建议在断言之前添加显式等待机制，并提出使用轮询辅助程序的修复方案：\n\n```python\nimport time\n\ndef wait_for_status(order_id, expected, timeout=5):\n    start = time.time()\n    while time.time() - start < timeout:\n        order = get_order(order_id)\n        if order.status == expected:\n            return order\n        time.sleep(0.1)\n    raise TimeoutError(\n        f\"Order {order_id} did not reach '{expected}' within {timeout}s\"\n    )\n```\n\n## 延伸阅读\n\n* *\n\n[GitHub Copilot CLI](/zh/copilot/how-tos/copilot-cli)"}