跳转到内容

cnb-rs api ​

cnb-rs api <path> [flags]

直接调用 CNB OpenAPI 的「逃生通道」,对齐 gh api 设计。给 extension、shell 脚本、调试场景一个零 Rust 依赖的入口,等价于 curl -H "Authorization: Bearer $CNB_TOKEN" ... 但额外提供模板变量替换、自动 base URL 拼接、自动 auth header、表单字段 → JSON body 转换。

参数 ​

  • <path>: API 路径。以 / 开头时自动 prepend https://{CNB_DOMAIN}/api;含 {owner} / {repo} 时自动替换为当前仓库(取自全局 --repo 或 git remote);完整 URL(https://...)直接透传

选项 ​

  • -X, --method <STRING>: HTTP method(默认有 body 时 POST,否则 GET)
  • -H, --header <K: V>: 追加 HTTP header,按 K: V 格式拆分(首个 : 之前为 key)。可多次使用
  • -f, --raw-field <K=V>: 字符串表单字段。多个 -f 合并为 JSON object body。可多次
  • -F, --field <K=V>: 带类型推断的表单字段。true / false → bool,null → null,纯数字 → number,其他 → string。可多次
  • --input <FILE>: 请求 body 来源。文件路径,或 - 从 stdin 读。与 -f / -F 互斥
  • --jq <EXPR>: 使用与其他 JSON 命令共享的 mini-jq 筛选响应(见下方「mini-jq 语法」)。复杂表达式请 pipe 到外部 jq
  • --paginate: 自动翻页 GET 请求,合并 JSON array 响应为一个大 array。响应非 array 时报错

继承的全局选项:

  • --repo <REPO>: 指定仓库路径(覆盖 {owner} / {repo} 模板的来源)
  • --domain <DOMAIN>: 指定目标域名(默认:cnb.cool)

行为细节 ​

Method 默认值 ​

  • 没传 -X + 没传 body → GET
  • 没传 -X + 传 -f / -F / --input → POST
  • 显式 -X 优先

Body 构造 ​

  • -f 和 -F 字段合并为 JSON object body(对齐 gh api)
  • --input 优先级最高,但不能与 -f / -F 同时使用
  • 自动设置 Content-Type: application/json(除非 -H "Content-Type: ..." 显式覆盖)

Auth Header ​

  • 自动加 Authorization: Bearer $CNB_TOKEN(来自配置 / keyring / 环境变量)
  • 自动加 Accept: application/vnd.cnb.api+json

模板变量 ​

仅支持 {owner} 和 {repo},其余 {...} 会报错。

  • {repo} → 完整 <group>/<subgroup>/<name> 三级路径
  • {owner} → 路径首段

输出 ​

  • 响应 body 以原始字节写到 stdout,不解析 JSON、不重格式化
  • 退出码:HTTP 2xx → 0;4xx / 5xx → 1(便于 if ! cnb api ... 判断)

mini-jq 语法 ​

api --jq 使用全局 mini-jq 语法:支持 .、.field、.parent.child、.field1,.field2、.items[]、.items[].field 和 .[].field。字段访问会自动映射数组元素,因此提取数组元素字段时可省略 .[]。单路径中字段不存在时返回 null,字符串结果输出裸字符串。

遇到管道 |、select(...)、length、数组下标等不支持的语法会立即报错。复杂查询请直接 pipe 到外部 jq:

bash
$ cnb-rs api /{repo}/-/issues \
    | jq -r '.[] | select(.state=="open") | .title'

--paginate 行为 ​

  • 仅适用于 GET 请求;其他 method 报错
  • 在 URL 上自动追加 ?page=N&page_size=100,循环 page=1,2,3,…
  • 每页响应必须是 JSON array;满 100 条继续,不满则停止
  • 合并所有页为一个大 JSON array 输出
  • 任一页 HTTP 非 2xx → 报错,不继续后续页
  • 与 --jq 组合:先合并再过滤

CNB 的 wrapped pagination(如 /{repo}/-/star-users 返回 {users: [...], total: N})不被 --paginate 自动支持,请 pipe 到 jq 自己拼。

不息(包括 4xx body)写到 stderr ​

不做什么(MVP 边界) ​

  • ❌ 不做 retry(用户用 shell while 自处理))
  • ❌ --jq 不内置完整 jq(复杂表达式 pipe 到外部 jq
  • ❌ 不做 GraphQL 包装(直接走 REST endpoint)
  • ❌ 不做缓存(未来 --cache <duration> 增加)

Windows Git Bash 注意 ​

Git Bash(MSYS)会把以 / 开头的参数当作路径转换成 Windows 路径(/user → C:/Program Files/Git/user),带查询串的路径(含 ?)通常不受影响,所以会出现「GET 带参数正常、POST 或无参数 GET 一律 404」的假象。cnb-rs api 会识别这种情况并报错提示。规避方式任选其一:

  • 写完整 URL:cnb-rs api https://api.cnb.cool/user
  • 设置环境变量:MSYS_NO_PATHCONV=1 cnb-rs api /user
  • 改用 PowerShell / cmd 运行

示例 ​

基本 GET ​

bash
# 拉当前用户信息
$ cnb-rs api /user

# 拉当前仓库的 issue 列表({repo} 自动从 git remote 推断为 group/subgroup/name 三级路径)
$ cnb-rs api /{repo}/-/issues

# 显式 --repo(不依赖 git remote)
$ cnb-rs --repo wwvo/cnb-rs/cnb-rs api /{repo}/-/releases/latest

POST + 表单字段(自动 JSON 化) ​

bash
# 创建 Issue
$ cnb-rs api -X POST /{repo}/-/issues \
    -f title="bug found" \
    -f body="repro: ..."

# 类型推断字段(true → JSON bool,123 → JSON number)
$ cnb-rs api -X PATCH /{repo}/-/issues/123 \
    -F state="closed" \
    -F priority=2

请求 body 从文件 / stdin ​

bash
# 从文件读
$ cnb-rs api -X POST /{repo}/-/issues --input data.json

# 从 stdin 读
$ echo '{"title":"x"}' | cnb-rs api -X POST /{repo}/-/issues --input -

# 与 heredoc 配合
$ cnb-rs api -X POST /{repo}/-/issues --input - <<EOF
{
  "title": "Hello",
  "body": "world"
}
EOF

自定义 header ​

bash
# 追加自定义 trace id
$ cnb-rs api -H "X-Trace-Id: my-trace-001" /{repo}/-/issues

# 覆盖默认 Accept
$ cnb-rs api -H "Accept: application/json" /{repo}

shell 包装:5 行 bash 写一个 cnb-whoami ​

bash
#!/usr/bin/env bash
set -e
cnb-rs api /user

把这 3 行存为 cnb-whoami,赋可执行权限,就可以用作 extension 入口。Phase 3+ 后可直接 cnb-rs ext install <owner>/cnb-whoami 调用。

内置 mini-jq 过滤 ​

bash
# 单字段(字符串结果不带 JSON 引号)
$ cnb-rs api /user --jq .username
alice

# 多字段
$ cnb-rs api /user --jq .username,.nickname,.repo_count
{
  "nickname": "Alice",
  "repo_count": 318,
  "username": "alice"
}

# 嵌套字段
$ cnb-rs api /{repo} --jq .owner.username

# 遍历 array 并提取字段
$ cnb-rs api /{repo}/-/issues --jq '.[].number'

自动翻页 ​

bash
# 拉所有 release(自动翻页)
$ cnb-rs --repo wwvo/cnb-rs/cnb-rs api /{repo}/-/releases --paginate

# --paginate + --jq 组合:列出所有 release 的 tag 名
$ cnb-rs --repo wwvo/cnb-rs/cnb-rs api /{repo}/-/releases --paginate --jq .tag_name

复杂场景 pipe 到外部 jq ​

cnb api 输出原始 JSON,复杂表达式可直接 pipe 给外部 jq:

bash
# 拉所有 open issue 的 title
$ cnb-rs api /{repo}/-/issues --paginate \
    | jq -r '.[] | select(.state=="open") | .title'

# 拉前 10 个 star 用户的 username(wrapped 分页,--paginate 不直接支持)
$ cnb-rs api /{repo}/-/star-users \
    | jq -r '.users[:10] | .[].username'

与 gh api 的差异 ​

维度gh apicnb-rs api
模板变量{owner} / {repo} / {branch}仅 {owner} / {repo}(CNB endpoint 多为 /{repo}/-/...,故 {owner} 用得较少)
输出默认 JSON 美化原始字节(用户自己 pipe 到 jq)
--jq flag内置 gojq(完整 jq)共享 mini-jq 子集(字段路径、多字段、[] 数组遍历),复杂表达式 pipe 到外部 jq
--paginate自动 Link header 翻页自动 ?page=N&page_size=100 翻页,仅支持 JSON array 响应
退出码2xx → 0;4xx/5xx → 1同
默认 method有 body → POST同

实现注意 ​

  • 复用 cnb-api crate 的 CnbClient::request_raw 公开方法,自动带 Authorization + Accept header + reqwest connection pool
  • 不复用 send_with_retry(cnb-api 内部的 GET 重试机制),保持「用户传什么 method 就发什么 method、不偷偷重试」的语义
  • HTTP 4xx / 5xx 不映射为 ApiError,而是把 status code + body 原样返回 + 退出码 1

Released under the MIT License.