npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hotsuitor/jenkins-cli

v3.0.0

Published

Jenkins 参数化构建 CLI + MCP 服务器: 选分支、触发构建、跟踪、下载产物、SSH 部署与回滚,并可供 Claude Code / Codex 调用

Readme

@hotsuitor/jenkins-cli

交互式 Jenkins 参数化构建 CLI(命令名 rbuild)。在终端里模糊搜索选分支、自动提取版本号、保存预设、一条命令触发构建并全程跟踪,不用再去网页上逐个填参数。

$ rbuild my-preset          # 用预设直接触发,全程跟踪到出包
$ rbuild                    # 交互模式: 模糊搜索选分支 -> 确认 -> 触发/存预设
$ rbuild my-preset --set portal=origin/hotfix   # 用预设,但临时换掉一个分支

安装

前置要求: Node.js >= 20。

npm install -g @hotsuitor/jenkins-cli

团队协作用法(推荐): 把 jobs.jsonpresets.json.env.example 放进团队自己的 git 仓库,在该目录下运行 rbuild——job 定义和预设随 git pull 全组同步。也可以直接 clone 团队仓库后 pnpm install(会自动编译 TypeScript)再 npm link 使用。

配置凭据

每个开发者用自己的 Jenkins 账号。在数据目录创建 .env:

JENKINS_URL=http://your-jenkins-host
JENKINS_USER=你的Jenkins用户名
JENKINS_TOKEN=你的API Token
  • API Token 生成: 浏览器登录 Jenkins → 右上角用户名 → 设置(Configure) → API Token → 添加新 Token
  • 也可以设置同名系统环境变量(优先级高于 .env)
  • .env 含个人凭据,记得加入 .gitignore

数据文件

rbuild 按以下顺序查找数据目录(以先找到 jobs.json 的为准):

  1. 工具安装目录(git clone + npm link 的团队仓库模式)
  2. 当前工作目录
  3. ~/.rbuild/

jobs.json(job 登记表)、presets.json(预设)、.env(凭据)、.rbuild-state.json(本地"上次使用"记录)都存放在数据目录。

使用

| 命令 | 说明 | | --- | --- | | rbuild | 交互模式: 选 job → 模糊搜索选分支 → 确认 → 触发 / 保存预设 | | rbuild <job\|预设名> | 预设名 = 直接触发(触发前自动校验分支是否仍存在);job id = 走交互填参 | | rbuild dl [job] | 下载产物到产物目录,缺省取最近一次成功构建 | | rbuild changes [job] | 查看构建的 git 变更记录(相对上一次构建的新提交),缺省最近一次完成的构建 | | rbuild deploy [服务器] | SSH 部署 .bin/.run/.tar.gz/.tgz 产物(见下文「远程部署」) | | rbuild rollback [服务器] | 回滚到远端备份 | | rbuild front deploy [机器...] | 把本地前端产物推到前端机(见下文「前端部署」),多选批量 | | rbuild front rollback [机器...] | 回滚前端到最近一份备份 | | rbuild front ls | 列出前端机清单 | | rbuild ls [jobs\|presets\|servers\|front] | 列出 job / 预设 / 后端服务器,缺省前三者 | | rbuild rm <预设名> | 删除预设 |

位置参数只表示"哪个目标"(job / 服务器 / 前端机)。"哪一版"——构建号、产物文件、备份名——一律走选项,所以解析器不需要靠值的形状去猜:

| 选项 | 说明 | | --- | --- | | --set 参数名=值 | 预填/覆盖构建参数,可重复。有 tty 时缺的照常问,无 tty 时缺就报错 | | --build <号> | dl / changes 指定构建号 | | --file <路径> | deploy 指定产物文件 | | --backup <名字> | rollback 指定备份 | | --dist <目录> | front deploy 指定前端产物目录(默认 ./dist) | | -o, --download | 构建成功后下载产物到产物目录 | | --out <目录> | 下载到指定目录(隐含 --download) | | --deploy[=服务器] | 构建成功后自动下载并部署(省略名字则弹列表选) | | -d, --detach | 拿到构建号就退出,不跟踪进度 | | --dry-run | 预览不执行:构建时打印将发送的参数,部署/回滚时打印执行计划(且不连远端) | | -y, --yes | 跳过确认提示 |

选项按命令白名单校验:不适用于当前命令的选项会直接报错,不会被静默忽略。rbuild <命令> -h 看某条命令自己的选项和示例。

产物目录

rbuild dl 默认把产物下载到 ./output/,rbuild deploy 不带 --file 时也从这里挑候选——一个目录两处生效,不会出现"刚下完的包 deploy 找不到"。

--out <目录> 单次改写,或在 .env 里设 RBUILD_ARTIFACT_DIR 长期改写。产物目录为空或不存在时,deploy 会回落到当前目录找。

Shell 补全

rbuild completion install        # 写入 ~/.rbuild/completion.*,确认后接入 ~/.bashrc 和 $PROFILE
rbuild completion bash           # 只打印脚本,自己决定放哪

支持 Git Bash 和 PowerShell(zsh 可用 bashcompinit 加载 bash 版)。补的东西:命令、当前命令允许的选项(用过的不再出现)、job id、预设名、后端服务器名、前端机名、--set 后的参数名、--file 后只留可部署产物。前缀无命中时退化为子串匹配,所以 rbuild front deploy 193<Tab> 直接给出 192.0.2.193

Tab 时不启动 node。 这台机器上 node 冷启动就要半秒,走 nvs shim 要 1.5 秒,任何"每次 Tab 调一次 rbuild"的方案都不可用。所以命令和选项在生成时写进脚本,预设/服务器名则由 shell 在 Tab 时直接读 JSON(PowerShell 用 ConvertFrom-Json,bash 用 grep——这几个文件里 name/id 键只在实体一层出现)。git pull 拿到新预设立刻可补;但 rbuild 升级后命令表可能变了,rbuild ls 会在末尾提醒你重跑一次 install。

completion install 会另外问一句要不要加 alias rbuild='node …/dist/src/cli/main.js':npm/nvs 给 Git Bash 的 sh shim 每次要起 uname/sed/cygpath 三个子进程,在 Windows 上是将近 1 秒;绕过它每次调用都省这一秒。PowerShell 的 shim 很轻,不需要。

产物下载自动携带你的 Jenkins 凭据(浏览器/wget 直接访问产物 URL 会因未认证被 403 拒绝),显示实时进度,先写 .part 临时文件、完成后原子改名,不会留半截文件。

交互模式的省事设计:

  • 分支列表支持输入关键字模糊过滤(如输 5.3.3)
  • 选完版本来源分支后,联动参数会把同版本分支排到最前
  • 版本号参数自动从分支名提取(origin/5.3.3/dev5.3.3),可手改
  • 时间戳参数每次触发时自动生成,无需关心
  • 上次用过的分支会标记 [上次使用] 并排在最前

触发后默认全程跟踪: 排队 → 构建中(实时耗时/预计时长) → 结束自动列出本次构建包含的 git 提交(哈希/作者/message) → 成功列产物下载链接,失败打印日志尾部。Ctrl+C 只断开跟踪,不影响 Jenkins 上的构建。

变更记录说明: 提交列表由 Jenkins 记录,含义是"相对该 job 上一次构建新增的提交";如果两次构建切换了分支组合,列表可能跨度较大。

预设共享

预设保存在数据目录的 presets.json。团队模式下提交推送后全组可用:

git add presets.json && git commit -m "preset: xxx" && git push

远程部署

在数据目录创建 servers.json 登记部署目标:

{
  "servers": [
    {
      "name": "prod-1",
      "host": "192.0.2.10",
      "port": 22,
      "username": "deploy",
      "password": "your-password",
      "remoteDir": "/opt/myapp/",
      "deploy": { "args": ["--noprogress"], "inheritEnvFromProcess": "chrome" },
      "keep": ["backups", "logs", "data"],
      "restart": { "containers": ["myapp", "myapp-config"] },
      "healthCheck": { "url": "http://localhost:8080/", "timeoutSec": 120 },
      "sudo": true
    }
  ]
}

| 字段 | 说明 | | --- | --- | | remoteDir | 部署目标目录,也用于识别安装器创建的备份 | | deploy | 可选部署策略覆盖。type 可设 installerarchive;args 是传给可执行安装包的参数;timeoutSec 是安装超时(默认 900 秒);inheritEnvFromProcess 按精确进程名从现有进程继承 GUI 环境白名单。通常依扩展名自动识别,无需配置 | | keep | 现场文件保护:不在构建产物里、但必须跨版本保留的相对路径(日志、运行时数据、历史备份、机器专属配置)。目录用移动、文件用复制 | | restart | 部署/回滚后重启服务。{"containers": [...]}docker restart;或用 {"command": "..."} 自定义(systemctl、启停脚本皆可) | | healthCheck | 重启后探活。url 用 curl 检查(默认接受 2xx/3xx,可用 expectStatus: [200] 指定);timeoutSec 默认 120 | | sudo | 允许在需要时提权(默认 false)。容器以 root 运行时,它写出的日志目录登录用户无权移动——移动目录需要对该目录本身有写权限(内核要更新其中的 .. 项),此时需要 sudo。密码取 sudoPassword,未配则复用 password |

keep 路径的搬迁有三级回退:mv(同文件系统改名,最快)→ sudo mv(保住原归属)→ cp(保底,归属变为登录用户)。三者都失败才报错并提示开启 sudo

没配 restart 的话,替换目录不会让服务用上新代码——进程仍持有旧文件句柄。工具会在这种情况下打印警告。

rbuild deploy                    # 弹列表选服务器和产物(可输入关键字过滤)
rbuild deploy prod-1 --file myapp-1.2.3.build.20260902.bin
rbuild deploy prod-1 --dry-run   # 只打印执行计划,不连远端、不改任何东西
rbuild my-preset --deploy        # 触发构建 -> 跟踪 -> 下载 -> 部署,一条命令

服务器、产物、备份、job 都可以省略不写:多个候选时弹出可输入过滤的列表,上次用过的服务器排在最前;只有一个候选时直接用。非交互环境(脚本/CI)必须显式指定,不会卡在提示上。

部署会按产物类型选择策略:

  • Linux 安装器 .bin/.run(推荐):SFTP 上传 → chmod +x → 预热 sudo 凭据 → 执行安装脚本并实时透传输出 → 合并历史日志 → rbuild 再做一次健康检查。安装器生成的 .backup.* 备份可直接由 rbuild rollback 使用。
  • 旧目录包 .tar.gz/.tgz(兼容):沿用解压、保留现场、原子换目录、重启与探活流程。

安装器默认参数是 --noprogress。以后安装器参数改变,只改 servers.json:

"deploy": { "type": "installer", "args": ["--quiet", "--accept"], "timeoutSec": 1200, "inheritEnvFromProcess": "chrome" }

如果将来升级成其他产物格式,只需在 src/core/artifacts.ts 登记扩展名并实现对应策略;CLI 选文件、Jenkins 下载后选包、部署确认和回滚不需要跟着修改。

安装脚本若会重启桌面程序(例如 Chrome),SSH 会话自身没有 DISPLAY。配置 inheritEnvFromProcess 后,rbuild 会在安装前从匹配的现有进程读取 DISPLAY/XAUTHORITY/DBUS_SESSION_BUS_ADDRESS/XDG_RUNTIME_DIR,脚本杀掉旧进程后仍能在正确桌面会话中启动新进程。

注入 DISPLAY 会踩到 makeself 自解压包的一个陷阱:它在"stdout 不是终端 + DISPLAY 有效"时,会 exec 一个图形终端重新执行自己。SSH 端由此立刻拿到退出码 0,安装却跑到了目标机的屏幕上——表现为部署报成功但版本没更新。rbuild 因此总会给安装包补上 --nox11(只关掉 makeself 这层自我重拉,不影响传给安装脚本的 DISPLAY),并在安装后核对目标目录确实发生了变化,退出码为 0 但目录没动时按失败处理。

旧目录包部署流程(设计为失败不伤旧环境):

  1. 产物上传到远端 /tmp/rbuild-deploy-<时间戳>/(SFTP,带进度)
  2. 在临时目录解压;包内单一顶层目录时用它作为新目录
  3. 保留现场:把 keep 里的路径从当前目录搬进新目录
  4. 旧目录改名备份为 <remoteDir>.bak-<时间戳>(解压成功后才动旧目录),新目录换入,只保留最近 3 份备份
  5. 重启服务(restart 配置)
  6. 健康检查(healthCheck):容器状态 healthy + HTTP 探活;不通过则自动回滚到部署前版本并再次重启(--no-rollback 可关闭,留现场排查)

keep 的目录采用移动语义,所以运行时数据(日志、历史备份)始终跟随当前版本,不会被塞进版本快照、也不会被备份轮转的 rm -rf 删掉。

回滚

rbuild rollback                 # 列出远端备份,交互选一个换回
rbuild rollback prod-1 --backup myapp.bak-20260814-160050
rbuild rollback prod-1 --backup myapp.backup.20260902_170129
rbuild rollback prod-1 --dry-run
rbuild rollback -y              # 跳过确认(脚本里用)

回滚同样是原子换名:当前版本先另存为新的 .bak-<时间戳>(所以滚错了可以再滚回来),再把选定备份改名换入,然后按 restart/healthCheck 重启并探活。keep 里的现场数据不跟着版本回退,继续留在当前目录。命令只接受该目录自己的 .bak-*.backup.* 备份,拒绝任意路径。

注意 servers.json 含登录口令,只应存放在内网私有仓库;更稳妥的做法是留空 password 字段并改用异构凭据下发(后续版本可加)。

前端部署

rbuild front deploy本地构建的前端产物(默认当前目录的 dist/)推到一批前端机,与整包部署是两套独立的清单和语义。

rbuild front deploy                    # 弹多选(空格勾选),上次推过的那批默认已勾选
rbuild front deploy 193 197            # 关键字直接命中机器名/IP
rbuild front deploy --dist ./build 193 # 产物不在 ./dist 时
rbuild front deploy 193 --dry-run      # 打印计划并做产物预检,不连远端
rbuild front rollback 193              # 回到最近一份备份;--backup 指定某一份

前端机登记在数据目录的 front-servers.json:

{
  "defaults": {
    "cleanList": ["assets", "locales", "favicon.ico", "index.html"],
    "healthCheck": { "url": "http://localhost:8090/", "timeoutSec": 30 }
  },
  "servers": [
    { "name": "web-1", "host": "192.0.2.21", "port": 22, "username": "deploy", "password": "your-password",
      "remoteDir": "/srv/app/front" }
  ]
}

替换语义是"合并覆盖",不是整目录替换:部署时先删掉 cleanList 列出的路径,再把产物解到 remoteDir 里。产物里有的同名文件会覆盖远端,cleanList 之外、产物里也没有的东西(其他子应用、现场配置)原样保留。defaults 对所有机器生效,单台可覆盖。

每台机器的流程:

  1. 前置校验:cleanList 里每一项在产物里必须存在且非空——要删远端的 X 就必须提供新的 X。这条规则从 cleanList 自动推导,专门防"构建没跑完就上传,把车端语言包清空"这类事故
  2. 本地 tar 打包(排除 *.map,保留 .gz/.br 预压缩件),上传到远端 /tmp
  3. cleanList 涉及的路径备份到同级的 <remoteDir>.bak-<时间戳>/(只备这几项,保留 3 份;不放 remoteDir 里面——那会被 nginx 当成可访问的 URL)
  4. cleanList → 解包覆盖
  5. 探活 healthCheck.url(能抓到解包文件权限不对导致的 403)

多台机器串行执行,某台失败(离线/密码错/探活不过)跳过继续,末尾汇总成败并以非零退出码结束。非交互环境必须显式给机器名。

回滚只还原 cleanList 涉及的路径,当前版本会先另存为新备份。

给 AI 工具用(MCP)

rbuild 自带一个 MCP 服务器,可以把触发构建、查状态/变更、下载产物、部署、回滚这些能力交给 Claude Code、Codex 等 AI 编码工具直接调用:

claude mcp add rbuild --scope user -- node /绝对路径/jenkins-web/dist/src/mcp/main.js

仓库根目录已带项目级 .mcp.json,在该目录启动 Claude Code 即自动加载。Codex 的 TOML 配置、11 个工具的清单、长任务与安全注意事项见 docs/mcp.md

想把自己的工具也包装成 MCP 服务,见 MCP 包装教程

新增 Jenkins job

编辑数据目录的 jobs.json:

{
  "jobs": [
    {
      "id": "my-job",
      "displayName": "我的打包任务",
      "jobPath": "/view/some-view/job/some-job",
      "params": [
        { "name": "AppVersion", "type": "version", "deriveFrom": ["main_repo"] },
        { "name": "BuildDate", "type": "timestamp" },
        { "name": "main_repo", "type": "gitBranch", "versionSource": true },
        { "name": "other_repo", "type": "gitBranch", "syncVersionWith": "main_repo" }
      ]
    }
  ]
}

| 字段 | 说明 | | --- | --- | | id | 短名,交互列表里展示 | | jobPath | Jenkins job 的 URL 路径(域名后、/build 前的部分) | | params[].type | gitBranch 分支选择(Git Parameter 插件) / version 版本号(自动提取) / timestamp 自动时间戳(yyyyMMdd-HHmmss) / text 自由文本 | | versionSource | 标在哪个分支参数上,表示从它提取版本号 | | syncVersionWith | 让该参数的候选列表按某参数的版本号置顶排序 | | deriveFrom | version 参数从哪些分支参数提取版本号(按顺序尝试) | | default | text 参数的默认值,交互时作为预填(上次用过的值优先级更高) | | optional | text 参数是否允许留空。留空时仍会把该参数发给 Jenkins,值为空串 |

文件上传类参数不需要登记,触发时留空。

从 1.x 升级

2.0 重排了参数语法。核心规则:位置参数只表示"哪个目标",不再表示"哪一版"。 1.x 靠值的形状(是不是纯数字、文件存不存在、含不含 .bak-)来猜第二个位置参数的角色, 而这些形状由输入和当前工作目录决定——同一条命令换个目录就可能做别的事。2.0 把这类角色全部改成显式选项。

| 1.x | 2.0 | | --- | --- | | rbuild dl 5x 490 | rbuild dl 5x --build 490 | | rbuild changes 5x 490 | rbuild changes 5x --build 490 | | rbuild deploy prod-1 app.bin | rbuild deploy prod-1 --file app.bin | | rbuild rollback prod-1 app.bak-2026 | rbuild rollback prod-1 --backup app.bak-2026 | | rbuild front 193 | rbuild front deploy 193 | | rbuild front ./build 193 | rbuild front deploy --dist ./build 193 |

写旧语法不会被误执行:多给一个位置参数会直接报错,并打印对应的新写法。

另外两处行为变化:

  • 产物默认下载到 ./output/ 而不是当前目录(见「产物目录」)。旧习惯不会失效——deploy 在产物目录为空时仍会回落到当前目录找包。
  • 预设名不能和 job id 重名了。保存预设时会校验并拒绝,因为 rbuild <名字> 这个位置同时接受 job id 和预设名。

代码结构

3.0 用 TypeScript 重写并分了三层。分层不是为了好看,是为了让同一份业务逻辑能同时服务两个完全不同的调用方:

src/core/   领域层。不含 console、不含 inquirer、不碰 process.exit。
            config/(四个 JSON 的加载与 zod 校验)  jenkins/(REST 客户端)
            build/(参数装配与构建跟踪)  deploy/(后端部署)  front/(前端部署)
src/cli/    终端外壳。参数解析、交互提示、彩色输出、退出码、补全脚本生成。
src/mcp/    MCP 外壳。同一套 core,换一个 Reporter 实现。

Reporter 是两层之间唯一的"说话"通道。 core 的函数都接一个 reporter 参数,只调 step/info/success/warn/error/detail/progress/stream 这几个方法。CLI 传一个往终端画状态行的实现,MCP 传一个收进数组的实现——这样 MCP 不必再劫持全局 console 来防止日志污染 stdio 协议流(2.x 为此在 5 处包了 capture())。测试传空实现。

几条配套约束,由工具而不是自觉来保证:

  • biomenoConsole 规则对全仓库生效,core 里写 console.log 会直接构建失败。
  • tsconfig 开了 strict + noUncheckedIndexedAccess + exactOptionalPropertyTypes,数组下标和可选字段都必须显式处理。
  • 四个 JSON 配置在读取时用 zod 校验,字段拼错会直接报出路径(servers.0.keep: 应为数组),而不是等到部署到一半才以"remoteDir 不合法"的形式冒出来。
  • test/golden/ 存着 2.2.0 的 help 与 dry-run 输出,npm test 会逐字节比对——重构可以随便改内部结构,但对外的每一个字都不许动。

开发

pnpm install        # 装依赖,prepare 钩子自动编译到 dist/
npm run dev -- ls   # 用 tsx 直接跑 TypeScript,不必等编译
npm run build       # tsc 编译
npm run typecheck   # 只查类型
npm run lint        # biome
npm test            # 编译 + 全部测试(含黄金比对)

常见问题

| 现象 | 处理 | | --- | --- | | 缺少 Jenkins 配置 | 按上文配置 .env | | 未找到 jobs.json | 在数据目录创建 job 登记表,见上节 | | 认证失败(401) | 用户名或 Token 错误,重新生成 Token | | 拒绝访问(403) | 账号没有该 job 权限,找 Jenkins 管理员开通 | | 返回 404 | jobs.jsonjobPath 写错 | | 无法连接 Jenkins | 检查内网/VPN | | 预设中的分支已不在...列表里 | 分支被删除/改名了,运行 rbuild 重选分支,存同名预设覆盖 | | Parameter xx provided value ... is invalid | 提交值不在该参数的实时分支列表(Jenkins 侧校验),同上处理 |

License

MIT