hmcommand-runner
v1.3.3
Published
Command suite 命令组调度与跨平台执行框架
Maintainers
Readme
hmcommand-runner
跨平台命令组调度与执行库。用 CommandDef 描述单条命令,按命名组编排顺序,统一支持 Shell 子进程、进程内函数与内置轻量操作。
安装
npm install hmcommand-runner快速开始
import { executeAll, CommandKind, type Groups } from "hmcommand-runner";
const groups: Groups = {
init: [
{ kind: CommandKind.Embed, run: "sleep 0.1", description: "预热" },
{ kind: CommandKind.Mac, run: "echo hello", description: "打印" },
],
};
const results = await executeAll(groups.init, { stopOnError: true });按组调度与重复执行:
import { runGroupChunks, getGroupChunks, parseGroupSpecs } from "hmcommand-runner";
const chunks = getGroupChunks(groups, parseGroupSpecs(["init:2", "build"], ["init", "build"]));
const { failed } = await runGroupChunks(chunks, { stopOnError: true, intervalMs: 100 });命令种类
每条命令通过 kind 选择执行通道。Shell 类可声明单个平台或平台数组;embed / builtin 仅支持单值。
| 种类 | kind | 执行方式 | 典型场景 |
|------|--------|----------|----------|
| Shell | linux / mac / windows 或其数组 | 启动对应平台 Shell 子进程 | 调用 CLI 工具、运行脚本、采集外部命令输出 |
| Builtin | builtin | 调用 func(context),进程内同步/异步 | 业务判断、读写共享上下文、按结果短路后续命令 |
| Embed | embed | 解析 run 字符串,进程内执行内置操作 | 固定延时、定时等待、删除本地文件 |
Shell
// 内联脚本(优先于 file)
{ kind: CommandKind.Mac, run: "git status", cwd: "/path/to/repo" }
// 可执行文件
{ kind: CommandKind.Linux, file: "/usr/bin/node", args: ["-v"] }
// 多平台同一条定义
{ kind: [CommandKind.Linux, CommandKind.Mac], run: "uname -a" }- 默认仅在当前宿主 OS 匹配
kind时执行;ignorePlatform: true时用当前 Shell 执行声明的命令。 - 支持
background/backgroundDelayMs(后台不阻塞队列)、captureToVar(抓取 stdout/stderr 到上下文)。 backgroundDelayMs > 0:调度成功即视为本条成功并立即继续后续命令;stopOnError与--continue-on-error均不因延后 spawn 或子进程后续失败而中断队列(详见下方说明)。run与file+args二选一;有run时走 Shell 脚本模式。
background 与 backgroundDelayMs
| 配置 | 队列行为 | stopOnError |
|------|----------|---------------|
| background: true,无 delay 或 backgroundDelayMs: 0 | 尽快 spawn,不等待子进程结束 | spawn 失败时本条失败,可 fail-fast |
| background: true,backgroundDelayMs > 0 | 不阻塞;调度成功即记成功并继续 | 不关注延后 spawn / 子进程结果,不回写失败 |
典型用法:先调度「80s 后执行的状态检测」后台命令,紧接着启动前台采集;两者并行,无需在宿主侧空等 80s。
Builtin
{
kind: CommandKind.Builtin,
description: "检查前置条件",
func(ctx) {
if (!ctx.get("ready")) {
return { shortCircuit: true, error: true, message: "未就绪" };
}
},
}func返回false记为失败;void/true为成功。- 返回
{ shortCircuit: true }跳过后续命令:error: true视同失败并中止,error缺省为中性短路(不计失败)。 - 通过
CommandContext在同批次命令间共享数据;后续 Shell 命令可用{{key}}模板引用字符串值。
Embed
进程内执行的轻量指令,通过 run 字符串声明,无需 Shell。详见 Embed 命令 章节。
Embed 命令
kind: CommandKind.Embed(或 "embed")时,执行器解析 run 字段并匹配内置指令。所有 Embed 命令在进程内运行、跨平台可用,不支持 background / backgroundDelayMs。
{ kind: CommandKind.Embed, run: "sleep 2", description: "组间间隔" }未识别的 run 或非法参数将返回失败(stderr 含 未知的内置命令)。
sleep
固定等待,阻塞当前命令队列直至到时。
| 项 | 说明 |
|----|------|
| 语法 | sleep <秒数> |
| 秒数 | 非负浮点数,如 0.5、2 |
| 成功时 | exitCode: 0,stdout / stderr 为空 |
{ kind: CommandKind.Embed, run: "sleep 1.5" }场景:组间固定间隔、等待外部资源就绪前的占位延时。纯倒计时且需交互跳过请用 timeout。
timeout
倒计时等待;TTY 下显示剩余时间,可按任意键提前结束。
| 项 | 说明 |
|----|------|
| 语法 | timeout <秒数>(兼容拼写 timieout) |
| 秒数 | 非负浮点数 |
| 成功时 stdout | timeout_elapsed(自然到时)或 stopped_by_key(按键跳过) |
| 终端输出 | 非静默时刷新 [timeout] 剩余 Ns,按任意键可跳过... |
{ kind: CommandKind.Embed, run: "timeout 30", description: "等待用户确认" }场景:需要人工介入的等待窗口(如「按任意键继续」)。无交互需求时用 sleep 即可。
unlink
静默删除本地文件;文件不存在或删除失败均不抛错、不阻断流水线。
| 项 | 说明 |
|----|------|
| 语法 | unlink <路径> 或 unlink <JSON 字符串> |
| 路径含空格 | 使用 JSON.stringify 包裹,如 unlink ${JSON.stringify(path)} |
| 成功时 | exitCode: 0(即使文件本不存在) |
{ kind: CommandKind.Embed, run: `unlink ${JSON.stringify("/tmp/cache.log")}` }
{ kind: CommandKind.Embed, run: "unlink ./output.tmp" }场景:流水线收尾清理临时文件。目录删除或复杂文件操作请用 Shell 或 builtin。
选用建议
| 需求 | 推荐 |
|------|------|
| 固定延时 | sleep |
| 可中断的倒计时 | timeout |
| 删除单个本地文件 | unlink |
| 条件判断、读写上下文、短路后续命令 | builtin |
| 调用外部 CLI、脚本、后台进程 | Shell |
组与命令套件
- Groups:
Record<string, CommandDef[]>,组名映射命令序列。 - GroupSpec:
name或name:N(重复 N 次),由parseGroupSpecs从 CLI--group解析。 - CommandSuiteModule:导出
groups、defaultGroupOrder,可选applyOptions(opts)按运行时选项生成组,供 CLI 注册为子命令。
import { resolveCommandSuite, runCommandSuiteGroups } from "hmcommand-runner";
const { groups, defaultGroupOrder } = resolveCommandSuite(commandSuiteModule, cliOptions);
await runCommandSuiteGroups({ groups, defaultGroupOrder, groupSpecs, ...runOptions });CLI 集成
registerCommandSuites 支持两种注册方式(可组合):静态模块(commandSuiteModules)与目录扫描(commandSuiteDirs)。同名子命令后注册的会被跳过。
静态注册(推荐用于打包/固定子命令集)
import { program } from "commander";
import { registerCommandSuites } from "hmcommand-runner/cli";
import * as reportMod from "./commands/report.js";
await registerCommandSuites({
program,
commandSuiteModules: [
{
commandName: "report",
module: reportMod,
displayPath: "commands/report.ts",
},
],
mergeCommandSuiteOptions: (opts, globals) => ({ ...opts, ...globals }),
});目录扫描(动态 import)
import { program } from "commander";
import { registerCommandSuites } from "hmcommand-runner/cli";
await registerCommandSuites({
program,
commandSuiteDirs: [{ dirPath: "/abs/path/to/commands", displayPrefix: "commands" }],
mergeCommandSuiteOptions: (opts, globals) => ({ ...opts, ...globals }),
});各命令套件可声明专属 CLI 选项,与全局选项合并后传入 applyOptions。JSON 输出中套件标识字段为 command_suite(schema_version 见 COMMAND_SUITE_CLI_RESULT_SCHEMA_VERSION)。
执行选项(常用)
| 选项 | 说明 |
|------|------|
| stopOnError | 任一条失败时中止本批剩余命令;runCommandSuiteGroups 与 CLI 默认 true(fail-fast)。例外:backgroundDelayMs > 0 且调度成功的后台命令不参与 fail-fast(见 Shell · backgroundDelayMs) |
| guaranteedOnFailureGroupNames | fail-fast 中止后补跑计划中尚未执行的组名(如 restore) |
| silent | 不向终端流式输出子进程 stdout/stderr |
| waitForBackground | 前台结束后等待或终止后台子进程 |
| ignorePlatform | Shell 命令忽略平台过滤,按当前宿主 Shell 执行 |
| intervalMs | 组块(chunk)之间的等待间隔 |
CLI 命令套件子命令默认遇错即停;需继续执行时使用 --continue-on-error。registerCommandSuites 可配置 guaranteedOnFailureGroupNames。
1.3.0 破坏性变更
自 1.3.0 起,原 Scenario* 命名统一为 CommandSuite*(如 resolveCommandSuite、registerCommandSuites、CommandSuiteModule)。CLI JSON 结果中 scenario 字段更名为 command_suite。升级时请同步替换符号与 JSON 字段名。
开发
npm install
npm run build
npm run test:all许可证
MIT
