如何在不安装 Node.js 的主机上用 Docker 官方镜像运行 Bruno CLI 集合并产出 JUnit 报告
如果你的 CI 构建机、临时测试机或受限环境里只有 Docker 而没有 Node.js / npm,又需要在机器上执行 Bruno API 测试集合并拿到 JUnit XML 报告,可以不用安装 Bruno CLI:Bruno 官方提供了 usebruno/cli Docker 镜像(Docker Hub 与 GHCR 两个 registry),把 Node.js 环境、bru 命令和整个运行时都装在了容器里。主机上只需要做两件事:把 Bruno 集合目录绑定挂载进容器,然后在镜像上执行 run 命令并指定 JUnit 报告输出路径。
前提条件(文档明确给出的部分):
- 主机已安装 Docker(镜像入口、用户和工作目录由官方镜像提供,不需要任何主机侧 Node.js);
- 一个 Bruno 集合目录(包含
.bru请求文件的目录)已保存在主机磁盘上; - 架构为
linux/amd64或linux/arm64,这是官方镜像发布的两种架构。
选择并拉取镜像
官方镜像同时发布到两个 registry,拉取命令等价:
docker pull usebruno/cli:latest # Docker Hub
docker pull ghcr.io/usebruno/cli:latest # GHCR
镜像有两个变体,按 Docker 镜像说明 的建议选择:
| 变体 | 基础镜像 | 选择依据 |
|---|---|---|
| Alpine(默认) | node:22-alpine |
默认选择,文档建议 90% 的用户用它 |
| Debian | node:22-slim |
只在遇到 SSL/glibc 兼容性问题时使用 |
tag 决定镜像版本,常见 pattern 有:
| Tag | 含义 |
|---|---|
latest / latest-alpine / alpine |
最新 alpine 发布 |
debian / latest-debian / <version>-debian |
debian 变体 |
3.3.0(即 <version>) |
精确版本,不可变 |
3.3(<major.minor>) |
跟随该小版本的补丁更新 |
3(<major>) |
跟随任意 3.x.x 发布 |
不带后缀的 tag(:latest、:3.3.0、:3 等)按约定始终解析为 alpine 变体。文档建议在 CI 生产环境固定精确版本,例如:
docker pull usebruno/cli:3.3.0
验证镜像可用
拉取后先跑一个最小命令确认 bru 能执行:
docker run --rm usebruno/cli --version
只要输出版本号即说明镜像工作正常。镜像的关键运行时属性(来自 镜像说明):入口命令是 bru,工作目录是 /bruno,以非 root 用户 node(UID 1000)运行。后面的命令都是利用这两点工作的。
运行集合并产出 JUnit 报告
以下命令假设你在集合目录下执行 docker,把当前目录挂载到容器内的 /bruno,bru 的参数直接跟在镜像名后面。
完整集合:
docker run -v $(pwd):/bruno usebruno/cli run
只跑集合内的某个子文件夹或单个请求文件:
docker run -v $(pwd):/bruno usebruno/cli run ./api-tests
docker run -v $(pwd):/bruno usebruno/cli run ./api-tests/login.bru
主路径——产出 JUnit XML 报告:
docker run -v $(pwd):/bruno usebruno/cli run --reporter-junit results.xml
--reporter-junit 后跟报告文件路径,这是 Bruno CLI 的命令行参数(见 Bruno CLI 选项表)。由于集合目录被绑定挂载到当前目录,生成的 results.xml 会直接落在你执行 docker 命令的那个主机目录里,无需额外拷贝。
几个使用细节:
$(pwd)适用于 Bash / Zsh / Git Bash / WSL;Windows 原生 PowerShell 换成${PWD},CMD 换成%cd%,例如docker run -v %cd%:/bruno usebruno/cli run;- 集合不在当前目录时,把挂载源换成集合的相对或绝对路径即可:
docker run -v /path/to/your/collection:/bruno usebruno/cli run,其中/path/to/your/collection需替换为你主机上集合目录的真实路径; - 文档中的示例未加
--rm,这样容器退出后仍可用docker logs/docker inspect排查;CI 里想避免堆积停止的容器时,给docker run追加--rm,它只影响容器清理,不影响挂载、输出和退出码; bru run的其余选项(-r递归、--env环境、--bail失败即停等)与本地 CLI 一致,可以按需在镜像名后继续追加。
可选分支:用 Docker Compose 同时产出多种报告
如果还要同时出 JSON、JUnit、HTML 三种报告,仓库里提供了一个现成的 compose 示例(packages/bruno-tests/docker-compose.yml,其中挂载了示例集合 collection/ 并跑 echo 子集):
services:
bruno-cli:
image: usebruno/cli:latest
container_name: bruno-cli-runner
volumes:
- ./collection:/bruno
- ./reports:/reports
command:
run echo
-r
--env Prod
--reporter-json /reports/results.json
--reporter-junit /reports/results.xml
--reporter-html /reports/results.html
执行 docker compose run bruno-cli。这里的关键是第二个挂载 ./reports:/reports:报告写到容器内 /reports 路径,通过该挂载落回主机 ./reports/ 目录。把 ./collection 和 ./reports 换成你自己的集合目录与报告目录(--env Prod 也要换成集合 environments 中真实存在的环境名),不需要某类报告时删掉对应的 --reporter-* 行即可。
可选分支:接入 CI 流水线
文档给出了两条 CI 路径,按需取用:
GitLab CI——直接用官方镜像作为作业镜像,此时在作业 shell 里直接执行 bru(入口就是它),并声明 JUnit 产物:
api-tests:
image: usebruno/cli:latest
script:
- bru run --output results.xml --format junit
artifacts:
reports:
junit: results.xml
GitHub Actions——runner 里已有 Docker,用 docker run 挂载 checkout 出来的仓库目录:
jobs:
api-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Bruno collection
run: |
docker run --rm \
-v ${{ github.workspace }}:/bruno \
usebruno/cli:latest run --output results.xml --format junit
- name: Publish Test Report
uses: dorny/test-reporter@v3
if: success() || failure()
with:
name: Bruno Test Results
path: ${{github.workspace}}/results.xml
reporter: java-junit
${{ github.workspace }} 是 GitHub Actions 内置变量,指 checkout 的仓库根目录,原样保留即可(要求集合就放在仓库根目录;否则把挂载源换成集合所在子目录)。注意这里用的是 --output results.xml --format junit 的写法——-f/--format 可选 json(默认)或 junit,与 --reporter-junit 是产出 JUnit 报告的两种等价参数形式。
结果验证与失败定位
一次运行可以从三个层面验证:
- 容器内终端输出:
bru run结束时打印运行汇总(请求数、通过/失败数)。smoke-test.sh 用它做过镜像级验证,其判定逻辑是:能输出汇总且至少 1 个请求通过才算镜像可用;汇总行形如Requests | 14 (12 Passed, 2 Failed)(新表格格式)或Requests: 14, Passed: 12, Failed: 2(旧行格式)。个别请求断言失败属于集合本身的结果,不代表镜像坏了。 - 主机上的报告文件:运行结束后检查
results.xml是否出现在你挂载的目录(或 compose 里的./reports/)中;接入 CI 后则由 runner 上传。 - 退出码:Bruno CLI 的退出状态码是文档定义的(见 Bruno CLI readme),容器退出码会原样透传,可用它区分“测试失败”和“跑不起来”:
| 退出码 | 含义 |
|---|---|
0 |
执行成功 |
1 |
集合中的请求、测试或断言失败 |
2 |
指定的输出目录不存在 |
3 |
请求链疑似无限循环 |
4 |
在集合根目录之外调用了 bru |
5 |
指定的输入文件不存在 |
6 |
指定的环境不存在 |
7 / 8 |
环境覆盖不是字符串/对象,或格式错误 |
9 |
请求了无效的输出格式 |
255 |
其他错误 |
对照退出码可以快速定位问题:报 4 说明挂载点不对(容器工作目录是 /bruno,集合必须映射到该目录);报 6 说明 --env 指定的环境名与集合中环境不匹配;报 9 说明 --format 传了 json/junit 以外的值。
限制与边界
- 镜像入口固定为
bru,无法通过该镜像执行bru以外的命令;需要容器内其他操作时要另行处理。 - 镜像以非 root 用户
node运行,挂载的集合目录需对该用户可读(写入报告同样要求挂载目录可写)。 - Alpine 与 Debian 变体内容一致,仅在基础镜像上有差异;SSL/glibc 问题切 Debian 变体时,记得 tag 也要换成
-debian后缀形式。 - 文档中所有示例都基于 Linux/macOS 风格的挂载语法;Windows CMD 需用
%cd%写法。
进一步的 CLI 参数细节(递归、环境、变量覆盖、报告头跳过等)可参考 Bruno CLI readme 的选项表,镜像构建(自行 docker build 指定 BRUNO_VERSION)可分别参考 Alpine 与 Debian 两个变体说明。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00