Appearance
核心概念
真正影响理解速度的,不是字段多不多,而是几个概念有没有分清
这一页只讲五件事
- workflow 是什么
- step 在哪里起作用
- values、results、events 分别放什么
- initialValues 和 defaultValue 有什么区别
- history、checkpoint、logs 该怎么选
workflow 是一条声明式执行链
clack-kit 的主线可以压成一句话
定义 workflow,按顺序执行 steps,把输入写进 values,把步骤产物写进 results,把运行过程记进 events
ts
import { defineStep, defineWorkflow } from 'clack-kit';
const workflow = defineWorkflow({
id: 'release',
steps: [
defineStep.text({
id: 'version',
message: '版本号',
required: true,
}),
defineStep.confirm({
id: 'dryRun',
message: '先 dry run 吗',
}),
],
});这段定义本身不会执行任何命令,也不会立刻产生交互
直到 kit.run 或 runWorkflow 被调用,workflow 才进入运行阶段
step 是执行单元,不是纯展示配置
每个 step 都有三个基础维度
- id 决定写入 values 或 results 时的 key
- kind 或 type 决定使用哪类处理器
- 配置字段决定展示方式、校验规则和运行行为
当前内置 step 可以分成三组
| 分组 | 类型 |
|---|---|
| 输入类 | text、password、path、date、select、selectKey、autocomplete、multiselect、autocompleteMultiselect、groupMultiselect、confirm |
| 展示类 | note、box、log |
| 执行类 | command |
输入类步骤通常写入 values,command 步骤通常把返回值写入 results,展示类步骤主要产生事件和终端输出
values、results、events 分别存什么
这三个容器一定要分清
| 容器 | 主要内容 | 最常见用途 |
|---|---|---|
| values | 输入值、最终配置、快照字段 | 条件判断、摘要、历史复用 |
| results | 步骤显式返回的业务结果 | 读命令步骤产物、后续业务判断 |
| events | 运行过程中的结构化事件 | 日志、分析、排障 |
values
适合放会影响后续步骤选择的内容
ts
if (result.values.environment === 'production') {
// ...
}results
适合放命令步骤或自定义步骤产生的结构化结果
ts
const image = result.results.buildImage;events
适合排查“这次运行到底发生了什么”
ts
const failed = result.events.find((event) => event.type === 'workflow-failed');initialValues 和 defaultValue 不是一回事
这两个名字很像,职责完全不同
| 字段 | 放置位置 | 作用 |
|---|---|---|
| initialValues | run 的入参 | 用外部值预填本次运行,常常直接跳过交互 |
| defaultValue | 单个 step 定义 | 给交互控件一个默认建议值,通常仍然进入交互 |
一个判断方法就够用
- 值来自 CLI、配置文件、环境变量,用 initialValues
- 值只是表单初始建议,用 defaultValue
history、checkpoint、logs 解决的是三类不同问题
| 能力 | 解决的问题 | 典型场景 |
|---|---|---|
| history | 下次能不能复用上一次输入 | 重复执行同类工作流 |
| checkpoint | 中断后能不能从现场继续 | 长流程、半自动流程 |
| logs | 运行完能不能回看过程 | 审计、排障、归档 |
history
保存的是一份输入快照,重点在 values
checkpoint
保存的是中断现场,除了 values 还会包含 results 和已完成步骤信息
logs
保存的是运行事件、输出、分析摘要和元数据
when 是条件流入口,同名步骤必须互斥
clack-kit 推荐把步骤平铺开,再用 when 控制命中
ts
defineStep.command({
id: 'prepareReact',
title: '准备 React 模板',
when: (context) => context.values.framework === 'react',
async run(_, io) {
io.stdout('prepare react\n');
},
});如果多条步骤使用相同 id,when 必须互斥
原因很直接,先命中的步骤会把这个 id 标记为已完成,后续同名步骤要么被跳过,要么覆盖结果
一张图看完整数据流
Loading diagram…