Skip to main content

诊断 CI 测试失败

用于 Copilot 命令行界面(CLI) 拉取 CI 日志、将故障关联到本地代码,并在不离开终端的情况下修复问题。

Copilot 命令行界面(CLI) 内置 GitHub MCP 服务器,使其能够直接访问你的 GitHub Actions 工作流运行记录、作业日志和检查状态。 结合对本地文件的访问权限,它可以提取 CI 故障详细信息,将它们与代码相关联,并从终端提出修补程序。

示例方案 1:测试在本地通过,但在 CI 中失败

你有一个测试用例,它在你的本地机器上可以通过,但在 CI 中却失败了。 你可以要求 Copilot 命令行界面(CLI) 直接调查此测试失败。

在此示例中,所测试的代码定义了一个简单的订单服务(order.py),并且有一个相应的测试来检查订单是否是今天创建的(test_order_service.py)。

示例提示

Copilot prompt
My CI is failing on this branch. Can you pull the latest workflow run 
logs, figure out what is failing, and help me fix it? The relevant files 
are @order.py and @test_order_service.py

示例响应

注意

以下响应是示例。 副驾驶聊天 的回答是不确定的,因此你可能会得到与这里所显示的不同的回答。

Copilot 命令行界面(CLI)使用 GitHub MCP 服务器提取当前分支上的最新工作流运行、标识失败的作业并检索其日志。 它发现以下故障:

___ TestOrderService.test_order_created_today ___
>       assert order["created_date"] == date.today()
E       AssertionError: assert datetime.date(2024, 1, 15) == datetime.date(2024, 1, 16)

test_order_service.py:45: AssertionError

读取完这两个本地文件后,Copilot 命令行界面(CLI)注意到日期恰好相差一天,并认定这是时区边界问题。 CI 运行程序使用 UTC,而本地计算机使用不同的时区。 测试在午夜附近运行时, date.today() 在每个环境中返回不同的日期。

Copilot 命令行界面(CLI) 建议通过注入时钟依赖项或固定时区来标准化 today 计算方式:

from datetime import timezone, datetime

def get_today():
    return datetime.now(timezone.utc).date()

它将该修复应用于 order.pytest_order_service.py 两者,并将所有引用更新为使用新的 get_today() 辅助函数。

修复后,可以验证更改并在本地运行测试:

  1. 运行 /diff 以准确查看 Copilot 命令行界面(CLI) 更改了什么。
  2. 运行 !pytest test_order_service.py 以确认测试在本地通过。
  3. 推送修补程序,让 CI 重新运行。

示例场景 2:存在竞态条件的间歇性测试失败

测试有时通过,有时在同一台计算机上失败。 你需要 Copilot 命令行界面(CLI) 比较成功运行和失败运行中的日志,以找出原因。

在此示例中,受测代码使用 order_service.py 中的后台作业异步更新订单状态,并在 test_order_service.py 中测试断言最终状态为 "processed"

竞态条件示例提示

Copilot prompt
This test passes sometimes and fails sometimes. Can you pull the logs 
from the last passing and failing CI runs for test_order_processed 
on this branch, compare them, and figure out what is going wrong? 
Relevant files: @order_service.py @test_order_service.py

竞争条件的响应示例

注意

以下响应是示例。 副驾驶聊天 的回答是不确定的,因此你可能会得到与这里所显示的不同的回答。

Copilot 命令行界面(CLI)使用 GitHub MCP 服务器列出当前分支上最近的工作流运行,识别出一个成功的运行和一个失败的运行,并获取这两个运行的日志。 它会比较它们:

传递运行:

[DEBUG] Created order #1234
[DEBUG] Background job started for order #1234
[DEBUG] Background job completed (52ms)
[DEBUG] Checking order status
[DEBUG] Order #1234 status: processed

失败运行:

[DEBUG] Created order #1234
[DEBUG] Background job started for order #1234
[DEBUG] Checking order status
[DEBUG] Order #1234 status: pending

Copilot 命令行界面(CLI) 发现,在通过的那次运行中,后台作业在状态检查之前已完成;而在失败的那次运行中,检查状态时作业仍在运行。 它将其识别为竞争条件,因为测试没有等待后台作业完成。

Copilot 命令行界面(CLI) 建议在断言之前添加显式等待机制,并提出使用轮询辅助程序的修复方案:

import time

def wait_for_status(order_id, expected, timeout=5):
    start = time.time()
    while time.time() - start < timeout:
        order = get_order(order_id)
        if order.status == expected:
            return order
        time.sleep(0.1)
    raise TimeoutError(
        f"Order {order_id} did not reach '{expected}' within {timeout}s"
    )

延伸阅读

GitHub Copilot 命令行界面 (CLI)