Cross-Repo Testing: SDK ↔ OpenHands Cloud 是一项用于对横跨 OpenHands/software-agent-sdk 与 OpenHands/OpenHands(云端后端)两个仓库的特性进行端到端测试的技能指南。
用途
该技能适用于需要端到端测试同时涉及 SDK 与 OH Cloud 后端的特性的场景,例如测试某个 SaaS 跨仓库功能、将特性分支部署到 staging、或让 SDK 针对 OH Cloud 分支进行测试。
仓库职责与数据流
指南给出了三个仓库的分工:
- software-agent-sdk:智能体核心,包含 openhands-sdk、openhands-workspace、openhands-tools 包,OpenHandsCloudWorkspace 位于此处。
- OpenHands:云端后端,包含 FastAPI 服务(openhands/app_server/)、沙箱管理、认证、企业集成,以 OH Cloud 形式部署。
- deploy:基础设施,包含 Helm charts 与 GitHub Actions,用于构建企业 Docker 镜像并部署到 staging/生产。
数据流为:SDK 客户端 → OH Cloud API(/api/v1/...)→ 沙箱 agent-server(运行于 runtime 容器内)。
两种流程的判断
根据依赖方向区分两种流程:
- Flow A — SDK 客户端 → 新云端 API:SDK 调用生产环境尚不存在的 API。示例:workspace.get_llm() 调用 GET /api/v1/users/me?expose_secrets=true。此流程只需部署服务端 PR。
- Flow B — OH 服务端 → 新 SDK 代码:云端服务端需要未发布的 SDK 包或新的 agent-server 镜像。服务端消费 SDK 中的新工具、智能体行为或 workspace 方法。此流程需要在服务端 PR 中把 SDK 固定到未发布的提交,并使用该 SDK PR 的 agent-server 镜像。
两种流程可能同时适用。
Flow A 步骤
A1. 编写并测试服务端改动:在 OpenHands 仓库实现新的 API 端点,运行单元测试(cd OpenHands; poetry run pytest tests/unit/app_server/test_<relevant>.py -v)。推送 PR,等待 "Push Enterprise Image"(Docker)CI 任务成功,该任务构建 ghcr.io/openhands/enterprise-server:sha-<COMMIT>。
A2. 编写 SDK 侧改动:在 software-agent-sdk 实现客户端代码,运行 SDK 单元测试(cd software-agent-sdk; pip install -e openhands-sdk -e openhands-workspace; pytest tests/ -v)。SDK CI 独立,单元测试不需要服务端改动即可通过。
A3. 将服务端 PR 部署到 staging。
A4. 针对 staging 运行 SDK E2E 测试。
Flow B 步骤
B1. 合并 SDK PR(或确定提交):SDK PR 必须 CI 通过以构建 agent-server Docker 镜像。镜像以 GitHub Actions 的 merge-commit SHA 打标签,而非 PR 中显示的 head-commit SHA。可在 SDK PR 描述中查找 AGENT_SERVER_IMAGES 部分,或在 "Consolidate Build Information" CI 任务中查找 "short_sha": "<tag>"。
B2. 在 OpenHands PR 中把 SDK 包固定到该提交:更新 3 个文件并重新生成 3 个 lock 文件(详见 update-sdk 技能)。包括 pyproject.toml 中在 dependencies 与 [tool.poetry.dependencies] 两处固定全部 3 个 SDK 包;openhands/app_server/sandbox/sandbox_spec_service.py 中使用 SDK 的 merge-commit SHA(AGENT_SERVER_IMAGE = 'ghcr.io/openhands/agent-server:<merge-commit-sha>-python');重新生成 lock 文件(poetry lock && uv lock && cd enterprise && poetry lock && cd ..)。
B3. 等待 OpenHands 企业镜像构建:推送固定后的改动,CI 会构建包含未发布 SDK 的新企业 Docker 镜像,等待 "Push Enterprise Image" 任务成功。
B4. 使用新的 OpenHands 提交 SHA 部署并测试。
B5. 合并前移除固定:CI 守卫 check-package-versions.yml 会在 [tool.poetry.dependencies] 含 rev 字段时阻止合并到 main。OpenHands PR 合并前,SDK PR 必须已合并并发布到 PyPI,然后将固定替换为已发布版本号。
部署到 Staging 特性环境
deploy 仓库会基于 OpenHands PR 创建预览环境。
方式 A — GitHub Actions UI(推荐):进入 OpenHands/deploy → Actions → "Create OpenHands preview PR" → 输入 OpenHands PR 编号。这会创建分支 ohpr-<PR>-<random> 并打开一个 deploy PR。
方式 B — 更新已有特性分支:在 deploy 仓库检出该分支后,在 .github/workflows/deploy.yaml 中同时更新 OPENHANDS_SHA(完整 40 字符提交)与 OPENHANDS_RUNTIME_IMAGE_TAG(同一提交加 -nikolaik 后缀)。更新前需验证企业 Docker 镜像已存在(可通过 gh api 查询 workflow_runs 并筛选 Docker,须显示 "Docker: success")。部署 CI 自动触发并在 https://ohpr-<PR>-<random>.staging.all-hands.dev 创建环境。可用 curl 检测 /api/v1/health,返回 401 表示服务已启动(需要认证)。首次部署 DNS 可能需要 1-2 分钟。
针对 Staging 运行 E2E 测试
重要:特性部署有各自的 Keycloak 实例,来自 app.all-hands.dev 或 $OPENHANDS_API_KEY 的 API key 无法使用,需要使用该特定特性部署的测试 API key,需由用户提供。测试可通过 OpenHandsCloudWorkspace 指定 cloud_api_url 与 cloud_api_key 进行,也可运行示例脚本(通过环境变量 OPENHANDS_CLOUD_API_KEY 与 OPENHANDS_CLOUD_API_URL 设置)。
记录结果
将测试输出推送到 SDK PR 的 .pr/logs/ 目录(使用 tee 写入日志,git add -f 后提交推送),并在两个 PR 上评论通过/失败摘要及日志链接。
注意事项
- 特性环境认证隔离:每个 ohpr-* 部署有自己的 Keycloak,生产 API key 不可用。
- deploy.yaml 中有两个 SHA:OPENHANDS_SHA 与 OPENHANDS_RUNTIME_IMAGE_TAG 都必须更新,runtime tag 为 <sha>-nikolaik。
- 企业镜像必须存在:OpenHands PR 的 Docker CI 任务须成功后才能部署,若未运行可推送空提交触发。
- DNS 传播:新分支首次部署需 1-2 分钟,后续部署即时生效。
- merge-commit SHA ≠ head SHA:SDK CI 以 merge-commit SHA 给镜像打标签。
- SDK 固定会阻止合并:check-package-versions.yml 阻止含 rev 字段的 PR 合并,SDK 须先发布到 PyPI。
- Flow A 使用原版 agent-server 即可:仅云端 API 变化时,OpenHandsCloudWorkspace 对接云端服务端而非 agent-server,无需自定义镜像。
- Flow B 需要 agent-server 镜像:服务端需要在 runtime 容器内使用新 SDK 代码时,必须固定到 SDK PR 的 agent-server 镜像。