Skip to content

ci(github): add GPU unittest workflow - #320

Open
SigureMo wants to merge 1 commit into
redai-studio:mainfrom
cattidea:codex/unittest-ci
Open

SigureMo wants to merge 1 commit into
redai-studio:mainfrom
cattidea:codex/unittest-ci

Conversation

@SigureMo

@SigureMo SigureMo commented Sep 16, 2026

Copy link
Copy Markdown
Member

What

Add a GPU unittest workflow that runs automatically for PRs targeting main and pushes to main, with a manual trigger for debugging.

Why

Run the full unit suite in the public Relax image on the existing H20 host runners, using the same dependency installation as interactive debugging.

How

  • Keep all steps in one workflow: create the container, prepare /workspace/Relax and show nvidia-smi, install requirements, run pytest, clean up, and upload results.
  • Pull ghcr.io/redai-studio/relaxrl:latest and log the actual image ID. Each runner supplies its fixed four-GPU allocation through CI_GPU_DEVICES; the container uses 32 GiB of shared memory.
  • Copy the exact Actions checkout into the container from a read-only mount. Keep container networking and IPC private so concurrent Ray/NCCL tests do not share ports or runtime state.
  • Share the host uv cache across runner instances. Install requirements with uv pip install --system --break-system-packages and the Tsinghua extra index.
  • Run every test before returning pytest's failure status. Preserve existing test skips, and use always() to remove the container and upload pytest logs/JUnit results.

Runner setup

Before enabling the workflow, set each runner installation's .env and restart that runner:

  • Labels: self-hosted, linux, x64, h20, gpu.
  • CI_GPU_DEVICES=0,1,2,3 for the first runner; CI_GPU_DEVICES=4,5,6,7 for the second runner on the same eight-GPU host.
  • Set http_proxy, https_proxy, and no_proxy as needed. The workflow forwards them into the container; image pulls use the Docker daemon's proxy.
  • uv cache defaults to the runner account's ~/.cache/uv (or $XDG_CACHE_HOME/uv). Set CI_UV_CACHE_DIR to override the shared host path.

Testing

  • pre-commit run --all-files --show-diff-on-failure: all hooks passed in Linux, including clang-format and gitleaks. The pinned clang-format binary cannot run on the local ARM Mac.
  • actionlint 1.7.12 and bash -n for the workflow shell blocks.
  • Executed the final YAML shell steps on four H20 GPUs: pytest returned exit code 1, the cleanup step removed the container, and JUnit/pytest logs remained on the host.
  • Full pytest suite: 2525 passed, 6 failed, 18 skipped on four H20 GPUs in the public image (374.20 seconds). All six failures are the existing strict floating-point assertions in tests/backends/megatron/test_chunked_mtp_loss.py; they are deliberately left for a separate PR. Existing skips are unchanged.

The first GitHub run successfully scheduled on the H20 runner and stopped before container creation because CI_GPU_DEVICES is not yet set in that runner environment. After completing the runner setup above, the check is expected to fail on the known MTP assertions until they are addressed. This PR does not modify framework code, dependencies, or tests.

Type of Change

  • CI/CD or build changes

# 🔧 CI/CD

## Run unit tests on host GPU runners

- Trigger GPU unit tests automatically for pull requests and pushes to main.
- Use the public latest image with a fixed four-GPU allocation per runner,
  a shared host uv cache, and a private container workspace.
- Keep dependency installation and the complete pytest run in separate
  workflow steps, preserving test failures and existing skips.
- Always remove the container and upload pytest logs and JUnit results.

Co-authored-by: Codex <noreply@openai.com>
Copilot AI lite review requested due to automatic review settings September 16, 2026 13:35
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-16T13:40:41.405828Z db2f18e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical runner-isolation issues and known failing tests must be addressed.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds a self-hosted H20 GPU unit-test workflow for pull requests, main pushes, and manual runs.

Changes:

  • Runs containerized GPU tests with uv dependency installation.
  • Uses fixed GPU allocations and shared caching.
  • Cleans up containers and uploads pytest artifacts.
File summaries
File Review summary
.github/workflows/unittest.yml Adds the GPU workflow and artifact handling. Critical concerns remain around executing untrusted PR code on persistent self-hosted runners and shared host state; the known six test failures also keep the workflow red.
Review details

Suppressed comments (1)

.github/workflows/unittest.yml:85

  • The command intentionally propagates pytest's exit status, but the PR's reported run already has six failures in tests/backends/megatron/test_chunked_mtp_loss.py. As written, every PR and main run will remain red, so this cannot be an automatic unit-test gate until that baseline is fixed or the workflow is deliberately prevented from blocking merges.
            python -m pytest tests/ -v -ra --tb=short --junitxml=/artifacts/junit.xml \
              2>&1 | tee /artifacts/pytest.log
  • Files reviewed: 1/1 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +6 to +7
pull_request:
branches: [main]
jobs:
unittest:
name: Unit Tests (H20, 4 GPUs)
runs-on: [self-hosted, linux, x64, h20, gpu]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

补充核验:这个 fork PR 的首次运行已经在 H20 runner 上完成 Checkout 并进入宿主机的 Create test container 步骤;缺少 CI_GPU_DEVICES 只会让当前脚本退出,并不是执行权限边界。PR 若修改这些 run 步骤,就能以 runner 账户直接使用 Docker,绕过第 49–64 行设置的 GPU、网络与挂载限制,影响同机另一 runner 及后续任务。

建议在启用公开 PR 自动执行前,把整个 runner 放进每次销毁的隔离环境;若保留持久化主机,则需要在 PR 无法修改的调度/runner 访问策略中限制为受信工作流和经审核的提交,单独增加本 YAML 内的条件不足以建立边界。GitHub 的自托管 runner 安全说明也指出了这种持久化风险。当前账户无法读取仓库级 fork 审批策略,因此这里不声称所有外部 PR 都会免审批运行;如已有外部约束,请提供其配置依据以便复核。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: db2f18ebd7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +6 to +7
pull_request:
branches: [main]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate untrusted PRs before using self-hosted runners

For a fork PR, the checkout contains contributor-controlled code that is executed by pytest on the persistent self-hosted H20 runner. The container still receives the runner's proxy values and writable shared uv cache, so malicious tests can exfiltrate embedded proxy credentials or poison state consumed by later trusted jobs; contents: read does not protect these host resources. Require trusted approval or an allowlist before scheduling fork code, or use an ephemeral isolated runner.

Useful? React with 👍 / 👎.

runs-on: [self-hosted, linux, x64, h20, gpu]
timeout-minutes: 30
env:
CI_IMAGE: ghcr.io/redai-studio/relaxrl:latest

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Configure and pin the CI image endpoint

Move this registry/image value—and the package-index endpoint on line 79—into repository or runner configuration rather than baking deployment-specific endpoints into the workflow; the current latest reference also lets the test environment change without a reviewed commit, making failures irreproducible. Pin the configured image by digest while preserving the logged image ID.

AGENTS.md reference: AGENTS.md:L69-L74

Useful? React with 👍 / 👎.

@rai-studio-bot

rai-studio-bot commented Sep 16, 2026

Copy link
Copy Markdown

Nyanpasu 审查看板

审查状态: 🚧 需要修改

审查版本: db2f18ebd756e27208080e5a2131c88337f4b947

再次核查 CI(claude 后端):head 与最近的 GPU 运行未变,仍为 2525 通过、6 项 test_chunked_mtp_loss.py 逐位相等断言失败、18 跳过。本轮把根因钉定:失败与 chunk 尺寸是否整除 S=16 完全对应——16、2 整除者通过,3、7 及 chunk_size=3 的用例不整除而失败,最大绝对差 3.7e-9 至 1.1e-8,属末块 GEMM/CE 形状变化引入的 fp32 舍入差,并非 chunked-MTP patch 的逻辑缺陷;建议改为容差比较并修正 BIT-FOR-BIT 措辞(详见本轮最新 PR 评论)。本 CI-only PR 未改动 patch 与测试,六项失败为既有断言、不构成新增阻塞项。原 F1(持久化 runner 信任边界)仍未解决。

编号 问题 优先级 状态 规则来源
F1 PR 可修改宿主机执行步骤,测试容器无法隔离持久化 runner P1 🚧 未解决 GitHub:自托管 runner 的安全边界
Powered by Nyanpasu with claude-opus-5-google[1m] medium, please check the suggestions carefully.

@rai-studio-bot rai-studio-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本轮暂不通过:持久化 H20 runner 的执行信任边界尚未得到保证,已在原讨论补充运行证据和修复建议。

已审查全部变更;本地 Shell 语法及失败退出码传递验证通过,现有 CPU CI / pre-commit 通过。GPU 检查正在重跑;本地未执行 GPU / 多节点集成测试,因为审查环境未配置相应硬件。PR 中报告的六项既有 MTP 断言失败未作为本轮新增阻塞项。

Powered by Nyanpasu with gpt-6-astra medium, please check the suggestions carefully.

@SigureMo

Copy link
Copy Markdown
Member Author

@rai-studio-bot 帮忙分析下 CI 失败问题

@rai-studio-bot rai-studio-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

针对 CI 失败分析请求,已核对 db2f18e最新 GPU 作业。这次容器创建、依赖安装、清理和结果上传均成功,失败发生在 pytest:2525 passed、6 failed、18 skipped,耗时 378.40 秒。首次运行缺少 CI_GPU_DEVICES 的问题已不再阻塞这次运行,测试日志与 JUnit已保留。

六项失败全部位于 test_chunked_mtp_loss.py

用例 失败断言 / 已打印的最大绝对差
test_chunked_mtp_matches_original[3] / [7] 第 133 行 torch.equal;分别 3.73e-9 / 1.86e-9
test_scale_logits_fn_per_chunk 第 148 行 torch.equal
test_calculate_per_token_loss_branch 第 158 行 torch.equal
test_is_training_logging_matches 第 197 行 torch.equal
test_sequence_parallel_gathers_hidden_once 第 420 行 torch.equal1.12e-8

最可能的原因是测试要求分块与整块浮点计算逐位相等。 这些用例实际使用 CPU fp32 的 mock head(矩阵乘法)及交叉熵;分块会改变运算形状,数学等价不保证逐位一致,参见 PyTorch 数值精度说明。当前日志不能进一步确定差异首先出现在 matmul、CE 还是后续计算。

其中 “gradient tensor mismatch” 比较的是 spy 捕获的前向 loss,并未调用 backward();SP 用例的 gather 次数和 flag 恢复断言已通过。因此这些报错不能直接解释为真实梯度、NCCL 或 H20 故障。该 PR 没有修改相关测试和实现,失败集合也与 PR 描述中的预跑结果一致。

建议仅调整 full-vs-chunk 的浮点值比较,并修正测试说明中的 BIT-FOR-BIT 承诺;保留层数、shape、mask 和 SP 行为等精确断言。例如以此容差作为同镜像验证的起点:

torch.testing.assert_close(g, r, rtol=1e-5, atol=1e-7)

先在本次 CI 镜像单跑 python -m pytest tests/backends/megatron/test_chunked_mtp_loss.py -v -ra,记录 logits、CE、归一化 loss 的绝对/相对误差,确认容差和后续 logging 断言,再跑全套。CPU CI 为绿色不代表这六项已通过:CPU 日志没有这些用例的执行记录,而该文件在 Megatron/Relax 导入失败时会整模块 skip。

本轮是诊断,未修改代码或重跑远端任务;本地 PyTorch 2.1 的简化 CPU 探针未复现差异,尚未在相同镜像验证修复。此前 runner 信任边界审查结论保持不变。

Powered by Nyanpasu with gpt-6-astra medium, please check the suggestions carefully.

@SigureMo

This comment was marked as duplicate.

rai-studio-bot

This comment was marked as duplicate.

@SigureMo

This comment was marked as duplicate.

1 similar comment
@SigureMo

Copy link
Copy Markdown
Member Author

@rai-studio-bot claude code backend 测试,你再看看 CI 问题呢

@rai-studio-bot

Copy link
Copy Markdown

已按请求再次核查 db2f18e 的 CI。head 与 GPU 作业 run 35102885455 均未变化,仍为 2525 passed / 6 failed / 18 skipped,失败集合与此前一致,全部落在 tests/backends/megatron/test_chunked_mtp_loss.py

这次比对日志后可以把根因钉得更死:失败与 chunk 尺寸是否整除序列长度 S=16 完全对应。

  • test_chunked_mtp_matches_original 参数是 [16, 2, 3, 7][16][2](整除 16)PASS[3][7](不整除,末块尺寸与其余块不同)FAIL
  • 其余失败用例(test_scale_logits_fn_per_chunktest_calculate_per_token_loss_branchtest_is_training_logging_matchestest_sequence_parallel_gathers_hidden_once)用的都是 chunk_size=3,同样不整除。
  • 打印的最大绝对差都在 fp32 舍入量级:chunk=33.73e-9chunk=71.86e-9、SP 用例为 1.12e-8

也就是说,当末块尺寸与其余块不同(3→3×5+17→7×2+2)时,该分块会走到不同的 GEMM/CE 计算形状,累加顺序变化带来逐位不同的舍入结果。分块的数学等价成立,问题出在 第 133/148/158/197/420 行torch.equal(逐位相等)比较——对浮点 full-vs-chunk 结果过于严格,不是 chunked-MTP patch 的逻辑缺陷

结论与此前 CI 分析一致:本 PR 只新增 workflow,未改动 patch 与测试,六项失败是既有断言、与 PR 描述预跑一致,不构成本次 CI-only PR 的新增阻塞项。

修复建议不变——改为容差比较,并同步修正文件头与用例里 BIT-FOR-BIT 的措辞(第 10 行第 119 行),保留层数/shape/mask/SP 行为等精确断言:

torch.testing.assert_close(g, r, rtol=1e-5, atol=1e-7)

请注意生产的 chunk 尺寸一般不会整除序列长度,因此正确做法是放宽浮点比较,而非仅挑选能整除的 chunk。建议先在同一 CI 镜像单跑 python -m pytest tests/backends/megatron/test_chunked_mtp_loss.py -v -ra 确认容差再跑全套。本轮为诊断,未改动代码或重跑远端任务;此前 runner 信任边界(F1)结论保持不变。

Powered by Nyanpasu with claude-opus-5-google[1m] medium, please check the suggestions carefully.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants