🏃 @cloud-cli/on
普通用途工作流程
为Node.js所制造的自备,轻量级,高性能的CI/CD跑跑发动机.
设计严格 安全第一边界,当地 JavaScript AST 评价,0-DSL模板文字,以及 a 内建终端日志网络仪表板。 。 。 .
关键要点
- 严格代码/数据分离 :
run:步骤作为原始进程脚本逐字执行。 表达式和动态数据绑定被严格隔离到env:,完全消除外壳注射向量. - 标准 ES 模板语法(
${...}: 没有自定义的 DSL 包装${{ }}或者说{{ }}。如果一个字段包含${...},它通过AST评价标准的JavaScript模板字符串逻辑. - 决定性外地评价: 没有静态倒置或模糊类型转换. 平面字符串仍为字面字符串;条件为
if:字段为严格的 JS 布尔表达式. - 已建入黑暗模式 Web UI (
/runs: 监测工作状态,检查工作空间输入,并查看实时提供的ANSI色终端日志流. - 系统及容器执行驱动程序 : 直接以离散主机进程组或被隔离的多克/系统瞬态单元内运行步骤.
- DB 授权的工作流程 : 通过认证的API起草、验证并公布便携式YAML工作流程.
- 计划执行 : 已发布的工作流程可以从克龙表达式或本地出日出/日出事件运行.
- 加密秘密存储 : 秘密密码文本被存储在数据库;主密钥仍保存在HTTP服务器上.
项目结构
my-project/
├── runner.config.mjs # Optional plugin/advanced configuration
└── package.json
QQ 快速启动
1. 安装和运行
直接通过引擎运行 npx 或者说 pnpm dlx数字 :
# Start each role (normally managed by the supplied systemd units)
npx @cloud-cli/on start-server
npx @cloud-cli/on start-scheduler
npx @cloud-cli/on start-workers
2. 界定工作流程
name: Build and Publish Release
on:
github:
events:
- push
owner: octocat
repo:
- example
- another-example
branches:
- main
- releases/*
paths:
- package*.json
if: inputs.action !== 'deleted'
concurrency:
group: release-${inputs.repo}
cancel-in-progress: true
# Every tag must be advertised by the worker that claims this workflow.
tags: [linux, docker]
steps:
- id: checkout
name: Checkout Code Repository
env:
CLONE_URL: ${inputs.clone_url}
COMMIT_SHA: ${inputs.commit_sha}
run: |
git clone --depth 1 "$CLONE_URL" .
git checkout "$COMMIT_SHA"
- id: install-and-build
name: Install Dependencies & Build
run: |
pnpm install
pnpm run build
- id: publish
name: Publish to NPM
env:
NPM_TOKEN: ${secrets.NPM_TOKEN}
run: |
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
npx --yes semantic-release@24 -b main --no-ci
将YAML作为草稿存储,并通过认证的API发布. 只有已出版的修订版才能收到webhoks或预定运行. 编辑马上进行测试,保存草稿和使用 快点跑; 此队列将保存的最新修改草稿不修改或发布:
curl -u admin:"$RUNNER_ADMIN_SECRET" -X POST http://localhost:11235/api/workflows/validate \
-H 'Content-Type: application/json' --data '{"sourceYaml":"..."}'
curl -u admin:"$RUNNER_ADMIN_SECRET" -X PUT http://localhost:11235/api/workflows/build-and-publish-release \
-H 'Content-Type: application/json' --data '{"sourceYaml":"..."}'
curl -u admin:"$RUNNER_ADMIN_SECRET" -X POST http://localhost:11235/api/workflows/build-and-publish-release/publish
时间触发器被定义在 Webhook 触发器旁边:
on:
schedule:
- id: nightly
cron: "0 2 * * *"
timezone: Europe/Berlin
solar:
- id: morning
event: sunrise
latitude: 52.52
longitude: 13.405
offset: +15m
GitHub 触发器支持这些预处理器过滤器 :
| 外地 | 匹配行为 |
|---|---|
events | 精确的 Webhook 事件名称 |
owner, (中文(简体) ), repo | 一个或许多确切可以接受的名称 |
name | 一个或许多确切可接受的名称,与Org/repo合并 |
branches | 分支/资源表型,例如 main 或者说 v1.*,匹配分支和标签 |
refs | 别名 branches |
tag | true 需要标签推; false 需要非目标事件 |
paths | 跨添加、修改并删除文件的已更改文件 glob 模式 |
配置的字段与AND结合. 一个列表中的值与OR结合. tags 还需要匹配标签值,同时 branches 需要匹配分支值。 一般情况 if 表达式在所有预处理器过滤器通过后分别进行评估.
例如,标签工作流程可以使用 tag: true 具有一个或多个正则表达式:
on:
github:
events: [push]
owner: octocat
repo: example
tag: true
tags:
- ^v1\..*
QQ CLI 使用和命令
npx @cloud-cli/on [command] [options]
图标
| 命令 | 说明 |
|---|---|
start-server | 运行 Webhook 入侵网关(接收webhooks的HTTP服务器). |
start-scheduler | 调度发布克龙和太阳工作流程触发器. |
start-workers | 运行事件驱动的工人调度器(可缩放的工人). |
CLI 和环境选项
| 旗帜 | 选项 | 默认 | 信封 | 说明 |
|---|---|---|---|---|
-h | --help | —— 说 | - 说吧 | 打印 CLI 帮助消息和退出 . |
-c | --config | ./runner.config.mjs | RUNNER_CONFIG_FILE | JavaScript 配置文件的路径 . |
-d | --database | - 说吧 | DATABASE_URL | 节点.js ES模块 URL导出数据库 API. |
-p | --port | 11235 | PORT | 入侵HTTP服务器端口. |
-k | --workers | 5 | RUNNER_WORKERS | 此节点的最大并行任务 . |
RUNNER_ADMIN_SECRET | 通过 API 刷新秘密的代理令牌 | |||
RUNNER_WORKER_SECRET | 工作活动和秘密检索的工人代号 | |||
OIDC_ISSUER | 例如 OIDC 发行器 URL https://auth.api.apphor.de | |||
OIDC_CLIENT_ID | 注册的 OIDC 客户端标识符 | |||
OIDC_CLIENT_SECRET | 已注册的 OIDC 客户端秘密 | |||
RUNNER_OIDC_PROVIDER_URL, (中文(简体) ), RUNNER_OIDC_CLIENT_ID, (中文(简体) ), RUNNER_OIDC_CLIENT_SECRET | 以上 OIDC 设置的后遗症别名 . | |||
RUNNER_SERVER_URL | 工人使用的 Webhook 服务器 URL . | |||
RUNNER_TAGS | 逗号分离工人能力标记. | |||
RUNNER_VAPID_PUBLIC_KEY | 背景Web Push通知的公钥. | |||
RUNNER_VAPID_PRIVATE_KEY | 背景Web Push通知的私钥. | |||
RUNNER_VAPID_SUBJECT | VAPID 联系人,例如 mailto:[email protected]。 。 。 . |
设定 RUNNER_ADMIN_SECRET 仅用于仪表板和管理API的HTTP服务器上. 设置相同的非空 RUNNER_WORKER_SECRET 在服务器和每个工人上发布工作状态更新事件并检索工作范围的秘密。 当三个 RUNNER_OIDC_* 数值被配置, /auth/login 允许浏览器登录入内,浏览器使用与外部消费者相同的无记名卡通 API 路径,认证用户可以读取日志,API 令牌设置 页面发布并通过OIDC 提供商取消被范围化的令牌. 登记册 https://your-runner-host/auth/callback 当OIDC重定向URI时. 仪表板工作流程 API 接受 Bearer 认证或 HTTP 使用用户名的基本认证 admin 还有管理员的秘密.
秘诀
秘密是写在 PUT /api/secrets/:NAME aES-256-GCM在数据库中加密。 设定 RUNNER_MASTER_KEY 仅在 HTTP 服务器上,或提供 on-master-key 系统证书。 工人只有在声称有工作时才得到解密的数值;数值不写给工作有效载荷或报告.
QQ 配置参考
您可以使用 runner.config.mjs 在您的工程根中 :
// runner.config.mjs
import { GitHubStatusPlugin } from "@cloud-cli/on";
export default {
port: 3000,
workers: 5,
serverUrl: "https://runner.example.com/",
tags: ["linux", "docker"],
storagePath: "/tmp/workspaces",
database: "https://remote.db.com/",
// Global environment variables passed to all steps
env: {
NODE_ENV: "production",
},
plugins: [
new GitHubStatusPlugin({
token: process.env.SECRET_GITHUB_TOKEN,
context: "on",
}),
],
};
GitHub 状态插件会在工作流程开始和完成时发布承诺状态 。 它的令牌需要权限来为目标寄存器写入承诺状态 .
背景通知
仪表板可以在通过Web Push关闭时收到工作通知. 生成 VAPID 密钥配对 npx web-push generate-vapid-keys,配置三个 RUNNER_VAPID_* 值,并启用 HTTPS 上的仪表板通知。 没有VAPID配置,通知只在仪表板打开时有效.
运行细节作为 HTML 提供 /runs/:id 并且被消毒的JSON at /api/runs/:id。在运行活动时, HTML 视图通过特定任务 SSE 事件进行被动更新。 两种表示都省略了原始的webhook体,敏感的输入字段,执行环境值,以及内部重运行状态.
打开 /workflows 以列出工作流程并管理秘密值。 打开 /workflows/new 以创建定义或 /workflows/:id 要编辑一个。 这些页面由管理员HTTP Basic Certification保护;秘密值只写.
确定性评价规则
为防止语法模糊和注入风险,工作流程定义中的字段在 三种严格模式数字 :
┌──────────────────────────────────────────────────────────────────────────┐
│ 1. RAW PASSTHROUGH MODE (`run:`) │
│ • Executed verbatim as a shell process command. │
│ • No string replacements or engine parsing performed. │
│ • Access environment variables strictly via shell syntax: $MY_VAR. │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ 2. EXPRESSION MODE (`if:`, `eval:`) │
│ • Evaluated strictly as pure JavaScript expressions via Acorn AST. │
│ • Must be valid JS syntax (e.g. `inputs.branch === 'main'`). │
│ • Automatically coerced to boolean in `if:` conditions. │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ 3. DETERMINISTIC VALUE MODE (`env:`, `name:`, `image:`, `group:`) │
│ • Plain strings WITHOUT `${}` remain 100% raw literal strings. │
│ • Strings WITH `${...}` evaluate as standard ES Template Literals. │
│ • Example: `node:${inputs.node_version}-alpine` │
└──────────────────────────────────────────────────────────────────────────┘
背景范围可用表达式
内部 ${...}, (中文(简体) ), if:,以及 eval: 上下文,以下对象范围被曝光: 1
inputs: 从接收到的webhooks收到的有效载重密钥值.env: 从全球配置和工作流程定义中合并环境变量.secrets: 来自加密中央密钥存储器的未显示任务范围值.steps: 执行状况和工作流程中以往步骤的产出steps.<id>.status, (中文(简体) ),steps.<id>.outputs) (中文(简体) ).files: 工作空间文件助手if:财务报告和审定财务报表eval:表达式 :files.exists('Dockerfile'), (中文(简体) ),files.readFile('package.json'),以及files.join('dist', 'artifact')。任务以外的路径workingDir拒绝.BUILTIN_HELPERS联合来文:公共事业String, (中文(简体) ),Number, (中文(简体) ),Boolean,以及JSON.parse编号 :JSON.stringify。 。 。 .
QQ Webhook 入侵网关和 Dashboard
Ingress Gateway为收到的HTTP请求收听并服务于活网络UI.
端点矩阵
| 方法 | 终点 | 说明 |
|---|---|---|
POST | /webhooks/github | GitHub 事件的 Webhook 端点 。 应用 GitHub 筛选和评价 on.github.if。 。 。 . |
GET | /runs | 盘子 : 公开直播暗模式监控页面列出近期工作. |
GET | /api/jobs?afterId=<id>&beforeId=<id>&limit=<n> | 带有独有的下端和上端ID光标并有从1到500的限制(默认为50)的 Dashboard 任务. |
GET | /runs/:jobId | 任务报告 : 经认证的交互式跟踪,并带有步时和终端日志输出. |
GET | /workflows | 认证工作流程列表并加密了秘密管理 UI. |
GET | /workflows/new, (中文(简体) ), /workflows/:id | 认证 YAML 工作流程编辑器 . |
POST | /api/workflows/validate | 已认证的 YAML 验证没有持久性 . |
GET, (中文(简体) ), PUT, (中文(简体) ), DELETE | /api/workflows/:id | 经认证的修订工作流程读取,草稿保存并删除. |
POST | /api/workflows/:id/publish | 最新修订草案的认证出版. |
POST | /api/workflows/:id/run | 已认证的当前保存的最新修订稿 . |
GET, (中文(简体) ), PUT, (中文(简体) ), DELETE | /api/secrets, (中文(简体) ), /api/secrets/:name | 已认证加密的秘密名称和只写值管理 . |
盘片特性
- 实时自动更新 :
/runs自动刷新任务队列状态( n)PENDING, (中文(简体) ),RUNNING, (中文(简体) ),SUCCESS, (中文(简体) ),FAILED, (中文(简体) ),CANCELLED) (中文(简体) ). - ANSI 终端渲染 : 用途
ansi_up在步骤日志框中准确渲染出出彩色、粗体亮度和控制输出. - 有效载荷检查: 查看从webhokes收到的 JSON 输入以方便调试 .
- 工作流程管理: 使用认证
/workflows创建,验证,发布,编辑,并删除 DB 授权的工作流程修改的页面.
安全加硬化
- 环境可变边界 :
强迫 shell 步骤通过进程环境变量消耗数据(
$CLONE_URL),含有外壳分解器(例如:.; rm -rf /) 无法变换 shell 脚本执行树 . - 原型污染保护:
AST评估明确阻止接触危险的JS财产(第5条)
constructor, (中文(简体) ),__proto__, (中文(简体) ),prototype) (中文(简体) ). - 有效载荷大小守护 : Ingress服务器执行严格的5MB有效载荷限制,以防止出记忆(OOM)拒绝服务攻击.
- 范围化 API 认证 :
工作流程验证、出版和秘密管理要求
RUNNER_ADMIN_SECRET过HTTPS。 OIDC 会话和提供商发行的可扩展符号在批准时读取工作日志logs:read工人使用单独的RUNNER_WORKER_SECRET只针对他们声称的生命周期事件和工作秘密.
部署
安装单元文件 systemd/ 并创建 root 拥有的环境文件 /etc/on/数字 : server.env 页:1 DATABASE_URL, (中文(简体) ), RUNNER_SERVER_URL, (中文(简体) ), RUNNER_ADMIN_SECRET, (中文(简体) ), RUNNER_WORKER_SECRET三个 RUNNER_OIDC_* 数值; scheduler.env 页:1 DATABASE_URL; 和 : worker.env 页:1 DATABASE_URL, (中文(简体) ), RUNNER_SERVER_URL, (中文(简体) ), RUNNER_WORKER_SECRET, (中文(简体) ), RUNNER_TAGS,以及 RUNNER_TMP工人只领取 RUNNER_WORKER_SECRET;管理员和OIDC秘密留在了HTTP服务器上. 提供单位作为根运行,因为系统执行驱动程序创建了瞬态系统服务. 使用 systemctl edit 用于每台机器的重置.
将服务器主密钥放入 /etc/on/credentials/on-master-key 仅限根权限. runner-server.service 通过Systemd的证书目录私下揭露. 启动主控制平面 systemctl enable --now runner.target; 启用 runner-worker.service 单靠工人机器 使用 systemctl edit runner-worker.service 用于机器专用标签和路径.
对于一揽子管理的远程工人,启用 runner-worker-update.timer每天4:00运行,每台机时最多45分钟,安装 @cloud-cli/on@latest 输入 /opt/on,然后只有在安装成功时才能重新启动工人. runner-worker.service 在重启期间,运行的工作最多可以排出一个小时。 不要让这个定时器用于一个由来源管理的发展工作者,而是从它的停工楼中部署这个工作者.
手动排出一个工人而不离开它停止,发送 SIGUSR1 仅用于主要进程: systemctl kill --kill-who=main --signal=SIGUSR1 runner-worker.service工人停止要求新的工作,等待工作完成,退出状态0,以及 Restart=always 开始替换。 使用 systemctl restart runner-worker.service (d) 系统等待相同的排水行为直至 TimeoutStopSec=1h。 。 。 .
工人现在在申请工作时解决了不可改变的修订问题。 一个工作流程可以安全使用一个退出步骤,然后 if: files.exists('Dockerfile')· ; files 被限制在工作 workingDir,因此无法检查该工作空间外的文件.
未来安全工作
这个MVP有意支持一个被信任的操作员. 在共享UI或将其曝光于私人部署之外之前,加入个人账户和角色,安全浏览器会话和CSRF保护,审计日志,工人注册证书,秘密密钥旋转,加密系统证书,限制费率,以及数据库的高可用性.
计划推出的运行者
一揽子管理的工人目前更新一个错开的每日计时器。 在允许自动推广新发布的跑道版本之前,实施受控的金丝雀推出:
- 发布一个新的不可移动 npm 版本,并解决候选人标签后面的确切版本.
- 在一个指定的金丝雀工人身上安装精确的版本,在推出时从不移动标记.
- 版本目录中的阶段发布和原子切换 a
current只有在安装和本地烟雾检查成功后才能连接. - 排出金丝雀与
SIGUSR1,重新启动,并保留先前的发布目录以回滚. - 要求金丝雀报告含有工人身份的心跳,运行版本,健康状况,以及最后一眼的时间.
- 运行一个专用
runner-health工作流程与tags: [canary]以验证队列要求、修订搜索、任务范围秘密检索以及执行驱动程序. - 记录推出状态持续:候选人版本、金丝雀工人、地位、晋升时间和失败原因.
- 只提升健康候选人
npm dist-tag add @cloud-cli/on@<version> stable。 。 。 . - 配置要安装的普通工人日更新
@cloud-cli/on@stable一个工人的货币限额. - 安装失败时, 重新启动、 心跳或健康检查, 标记被拒绝的候选人, 离开
stable不变,然后把金丝雀卷回原来的版本.
SSE应该通知工人有更新信息,但控制飞机数据库必须仍然是权威的推出状态,因此断开连接的工人在重新连接后安全地汇合.
发展
pnpm i
pnpm run lint
pnpm run test
pnpm run build