当 CI/CD 平台需要服务数百万个代码仓库时,问题不只是“如何执行一条构建命令”。平台还必须处理任务隔离、构建产物、失败重试、权限边界、流程定制,以及不同团队对流水线的差异化需求。将复杂 YAML 配置拆成 TypeScript 工作流步骤,再结合 Cloudflare Workflows、Artifacts 和 CI SDK,可以把流水线从静态配置变成可编排、可观察、可扩展的执行系统。
从 YAML 配置转向 TypeScript 工作流
YAML 适合描述简单的声明式流程,但当流水线包含条件分支、动态矩阵、外部服务调用、失败恢复和上下文传递时,配置文件很快会变成难以维护的“程序”。TypeScript 工作流步骤可以直接使用类型检查、函数抽象和测试工具,把构建逻辑放回熟悉的应用代码中。
一个典型的 CI 工作流可以拆成以下步骤:
- 检出指定仓库和提交。
- 创建隔离的构建沙箱。
- 安装依赖并执行测试。
- 生成并保存 Artifacts。
- 发布状态,或将失败交给修复代理处理。
下面是一段可改造的示意代码。具体导入路径和方法名需要以当前 Cloudflare Workflows、Artifacts 与 CI SDK 版本为准,但编排结构可以直接作为项目骨架使用:
// src/workflows/ci.ts
// 示意代码:请按当前 Cloudflare CI SDK 的实际 API 调整 import 和方法名。
import { WorkflowEntrypoint, WorkflowStep } from "cloudflare:workers";
import { createSandbox } from "@cloudflare/ci";
import { artifacts } from "@cloudflare/artifacts";
type Env = {
CI_REPOSITORY: string;
CI_TOKEN: string;
};
type TriggerInput = {
repository: string;
commit: string;
command?: string;
};
export class PullRequestCI extends WorkflowEntrypoint<Env, TriggerInput> {
async run(event: TriggerInput, step: WorkflowStep) {
const source = await step.do("checkout", async () => {
return {
repository: event.repository,
commit: event.commit,
};
});
const result = await step.do("test in isolated sandbox", async () => {
const sandbox = await createSandbox({
network: "restricted",
timeoutSeconds: 900,
environment: {
CI: "true",
CI_COMMIT_SHA: source.commit,
},
});
await sandbox.checkout(source.repository, source.commit, {
token: this.env.CI_TOKEN,
});
const command = event.command ?? "npm ci && npm test";
return sandbox.exec(command);
});
await step.do("save build artifacts", async () => {
await artifacts.upload({
name: `ci-${source.commit}`,
files: ["coverage/**", "dist/**", "test-results/**"],
metadata: {
repository: source.repository,
commit: source.commit,
exitCode: result.exitCode,
},
});
});
return {
repository: source.repository,
commit: source.commit,
passed: result.exitCode === 0,
logs: result.stdout,
};
}
}
这段流程的关键不在于把所有步骤写进一个函数,而在于让每个步骤具备清晰的边界。Workflows 可以负责步骤编排和持久化执行状态,CI SDK 可以负责沙箱内的代码执行,Artifacts 则负责保存测试报告、覆盖率文件和构建输出。
沙箱是多租户 CI 的边界
在平台化 CI/CD 中,仓库代码并不完全可信。构建脚本可能会读取环境变量、访问网络、消耗大量 CPU,甚至尝试修改宿主环境。因此,执行构建任务时应把沙箱视为平台的主要安全边界,而不是一个可选优化。
可以把以下约束作为默认策略:
- 只注入任务真正需要的环境变量,避免把平台凭据全部暴露给构建进程。
- 默认限制外部网络访问,只开放依赖下载和必要的服务地址。
- 为每个任务设置超时、CPU、内存和磁盘限制。
- 将仓库代码、缓存、日志和 Artifacts 分开管理。
- 将仓库、提交和执行 ID 写入元数据,便于审计和追踪。
生产环境中还需要区分“构建沙箱”和“控制平面”。控制平面负责接收 webhook、决定执行哪些步骤和更新状态;沙箱只负责运行不可信代码。不要让构建脚本直接获得能够创建其他工作流、读取任意 Artifacts 或修改平台配置的权限。
Artifacts 不是日志的替代品
日志适合实时查看过程,Artifacts 适合保存需要在任务结束后继续使用的结果。两者应当分开设计:
- 日志:编译输出、测试过程、失败堆栈和诊断信息。
- Artifacts:覆盖率报告、二进制文件、容器清单、测试结果和调试包。
- 元数据:仓库、提交、分支、运行 ID、工具版本和退出码。
例如,可以用统一的目录约定让不同语言项目都能上传结果:
set -eu
mkdir -p ci-artifacts
if [ -d coverage ]; then
cp -R coverage ci-artifacts/coverage
fi
if [ -d dist ]; then
cp -R dist ci-artifacts/dist
fi
find . -maxdepth 3 -type f \
\( -name "junit*.xml" -o -name "test-results*.xml" \) \
-exec cp --parents {} ci-artifacts/ \; 2>/dev/null || true
tar -czf ci-artifacts.tar.gz ci-artifacts
printf 'artifact=%s\n' "ci-artifacts.tar.gz"
这类脚本可以作为不同项目的默认收集步骤,再通过 CI SDK 的上传能力保存到 Artifacts。平台还可以为每个 Artifact 设置保留时间和访问权限,避免构建产物无限累积,或被不相关的仓库读取。
让 AI 代理负责诊断,而不是直接放权
自愈流水线的价值不在于“失败后自动重跑”,而在于系统能够识别失败类型,收集足够上下文,并执行有限、可审计的修复动作。AI 代理可以分析测试日志、最近的代码变更和历史运行结果,然后提出修复建议或生成补丁。
一个实用的代理提示词可以明确输入、输出和权限边界:
你是 CI 诊断代理。
输入:
- 仓库:{{repository}}
- 提交:{{commit}}
- 失败步骤:{{step}}
- 最近 200 行日志:{{logs}}
- 相关 Artifact:{{artifact_manifest}}
任务:
1. 判断失败属于代码错误、依赖错误、基础设施错误还是暂时性错误。
2. 给出证据,引用日志中的具体信息。
3. 只有在判断为暂时性错误时,建议一次重试。
4. 只有在获得显式授权时,才生成代码补丁。
输出 JSON:
{
"category": "code|dependency|infrastructure|transient|unknown",
"confidence": 0.0,
"evidence": ["..."],
"action": "retry|comment|create_patch|stop",
"reason": "..."
}
代理的执行结果应回到工作流中,由确定性的策略决定下一步,而不是让模型直接调用任意平台 API。例如,retry 可以限制为一次;create_patch 只能创建临时分支和 Pull Request;stop 则保留原始日志和诊断结果。这样既能利用 AI 的分析能力,又不会把生产控制平面交给不可预测的自动化逻辑。
落地时的检查清单
- 用 Workflows 表达长时间运行、可重试和可观察的步骤。
- 用 TypeScript 抽象公共流程,把仓库差异收敛为输入参数或插件。
- 用 CI SDK 在隔离沙箱中执行仓库代码。
- 用 Artifacts 保存可复用的构建结果,并配置保留策略。
- 为每次运行建立仓库、提交、运行 ID 和步骤状态的关联。
- 对 AI 代理设置明确的工具权限、输出格式和人工审批边界。
- 先从测试和静态检查开始,再逐步加入发布、部署和自动修复。
这种架构的主要收益是定制能力和平台控制力,但代价也很清楚:平台团队需要维护沙箱生命周期、资源配额、缓存策略、凭据管理和失败诊断。对于规模较小、流程高度固定的团队,现成 CI 服务可能更简单;当你需要把 CI/CD 变成产品能力,并让大量仓库共享一套可编程执行平台时,Cloudflare 上的 Workflows、Artifacts 和 CI SDK 才更有发挥空间。