跳转到内容

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 / --inputPOST
  • 显式 -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 语法

--jq 接受最简版 jq 子集,cover 90% 字段提取场景:

表达式行为
. 或空字符串identity,保留原 value
.fieldobject 取字段(结果是含此单字段的 object)
.field1,.field2object 多字段(输出 object subset)
.parent.childobject 嵌套字段(保留 {parent: {child: ...}} 结构)
上述任一应用于 array对每个元素递归过滤

遇到信支持的语法(管道 |select(...)length.[] 等)立刻报错,避免 silent 返回空。复杂查询请直接 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> 增加)

示例

基本 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
# 单字段
$ cnb-rs api /user --jq .username
{
  "username": "alice"
}

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

# 嵌套字段(保留 wrapper 结构)
$ cnb-rs api /{repo} --jq .owner.username

# 应用到 array(对每个 item 过滤)
$ cnb-rs api /{repo}/-/issues --jq .number,.title,.state

自动翻页

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 子集.field / .a.b / .x,.y),复杂表达式 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.