角色
个人项目
状态
私有/本地
版本
local tag v0.5.0
关键结果
类型化 findings 与受控操作,落在同一份权威的 launchd 状态上。
技术栈
RustCLImacOS

是什么

ghrunners 会找出一台 Mac 上所有 actions-runner (跑 GitHub Actions 任务的执行器),交叉核对 launchctl 域状态、进程树、.runner JSON、worker 日志,以及 (可选的)GitHub API。观测核心——statusdescribedoctorlogs——会把 查出来的问题整理成一条条类型化的 finding;另外还有个独立、受控的 control 子命令,动的也是同一份状态。它是个私有的本地 工具,它具体对应哪个快照,写在下面的「证据」里。

launchd + ps + logs + api → findings → control launchd ps logs api findings control
图 3.1 — 四路证据 → 类型化结论 → 受控的 launchd 控制

要解决的问题

Mac 上一台 你自己运行、用来执行 GitHub Actions 任务的机器,区别于 GitHub 托管的云端 runner——操作系统、网络,以及任务之间保留什么,都由你掌控。 悄无声息地挂掉时,线索是散落 各处的:macOS 的系统服务管理器——负责启动、停止、监督后台服务(大致相当于 Linux 上的 systemd)。 记着域和上次退出状态,进程树才知道 worker 是不是真活着,安装目录用 0700 权限把配置挡在外面, 而 GitHub API 才知道任务到底有没有真正派到这台机器上。你 SSH 上去一点点拼, 可最顺手的修法——重启 runner——恰恰最危险:一条不小心的命令, 就可能干掉正在跑的构建,或者把它 bootstrap 进错误的 launchd 域。

约束与关键决策

单次观测,基于权威状态。它不跑监控在任务之间常驻后台的程序。ghrunners 刻意反其道而行——一次性运行:跑一次、做完动作就退出,不保留长期状态。,每次都是无状态调用,去解析 launchctl print 的正文——读它不需要 sudo。所以哪怕 是非 root 调用,它报出来的运行态和上次退出状态也是权威的,而不是 拿 ps 猜的。代价:单次运行也意味着 没有持续监控、没有历史趋势——想要快照就跑一次,或者交给 cron 定时跑。

部分输出,而不是一遇错就停。runner 的安装 目录在别人的用户名下、权限 0700,严格一点的工具 没有 sudo 就直接报错退出。这里换成优雅降级: PID -REPO ? 不算错误,读不动的 路径变成一条 Unreadable finding,而不是直接崩掉; 与此同时,state 列仍然从不需要 sudo 的 print 正文 里读,所以保持权威。代价:表格里每一个空着的格子,工具都得设计好它的含义、 解释清楚——到底是权限不够,还是真的没有——而且少了 sudo,输出就不那么完整。

受控的 control,绑在它观测到的状态上。最顺手的 下一步——start / stop / restart——恰恰是危险的那一步,所以 control(bootstrap / unload / restart)的目标,是从 观测核心那份权威状态里解析出来的,而不是随手敲 launchctl--dry-run 只打印解析出来的 命令,并不执行;除了 DoubleLoaded 情况下的 unload, --domain 一律按用法错误拒绝(其余每个 verb 的目标 都是确定的,再去覆盖只会搞乱);而在某个 verb 可能干掉正在 运行的 Runner.Worker 之前,必须显式传 --yes代价:要多记一串前置条件和 参数,而且有些直接用 launchctl 照样能跑的命令,工具 会故意拒掉。

类型化 findings 和稳定的退出码,而不是大段日志。 「这个 runner 健康吗」需要机器读得懂的答案,所以把问题归纳成 13 种带严重度的类型化 findings——doctor 只打印 可操作的 warn / error,每条带一行 fix:——而且 statusdoctor 一看到 warning 或 error,进程就以 1 退出,于是 ghrunners doctor >/dev/null || mail … 能当 cron 健康检查用。代价:每一条 finding 都是一份 类型化契约,要定义、要触发、要标严重度、要给修法——是一套需要 维护的分类目录,而不是随手写的自由文本。

证据

私有快照 local tag v0.5.0——没有可查看的公开源码。 支撑这些说法的,是真正跑起来的功能:statusdescribedoctorlogs 和受控的 control 现在都能用,背后是一份 13 种 类型化 findings 的目录(从 LoadedNotRunningOrphanListenerDoubleLoadedUnreadable),外加 cron 友好的退出码、JSON 输出, 以及每个控制动作都给的 --dry-run 预览。这套能力是 一步步落地的:v0.2 加了控制 verb,v0.3 让运行态从 print 正文里 读出来、变得权威,v0.4 加了 doctor 巡检和 OrphanListener。

下一步

下一步还没完全敲定;候选项包括把 GitHub API 接得更深,以及把 真实机器上冒出来的边角情况沉淀成新的 findings。等仓库能公开 访问,再加源码链接。

它不是什么

  • 不是 runner 安装或注销工具。
  • 不是 daemon 或持续监控器。
  • 不是 fleet 管理——控制是单 runner、受控的,不是批量编排。
  • 不支持 Linux 或 Windows。
  • 私有 / 本地——目前还不是公开源码项目。