buildkite/go-pipeline

GitHub: buildkite/go-pipeline

Buildkite 官方的 Go 库,用于在代码中解析、构建和修改 CI/CD 流水线配置,支持 YAML/JSON 序列化与字段顺序保留。

Stars: 12 | Forks: 3

# go-pipeline [![构建状态](https://badge.buildkite.com/1fad7fb9610283e4955ea4ec4c88faca52162b637fea61821e.svg)](https://buildkite.com/buildkite/go-pipeline) [![Go Reference](https://pkg.go.dev/badge/github.com/buildkite/go-pipeline.svg)](https://pkg.go.dev/github.com/buildkite/go-pipeline) `go-pipeline` 是一个 Go 库,用于在 golang 中构建和修改 Buildkite pipeline。[Buildkite Agent](https://github.com/buildkite/agent) 在内部使用它在上传前检查和签名 pipeline,但它对于构建生成 pipeline 的工具也非常有用。 ## 安装 要安装,请运行 ``` go get -u github.com/buildkite/go-pipeline ``` 这会将 go-pipeline 添加到你的 go.mod 文件中,并使其在你的项目中可用。 ## 用法 ### 从 yaml 加载 pipeline ``` const aPipeline = ` env: MOUNTAIN: cotopaxi COUNTRY: ecuador steps: - command: echo "hello world" - wait - command: echo "goodbye world" ` p, err := pipeline.Parse(strings.NewReader(aPipeline)) if err != nil { panic(err) } pretty.Println(p) // &pipeline.Pipeline{ // Env: &ordered.Map[string,string]{ // items: { // {Key:"MOUNTAIN", Value:"cotopaxi", deleted:false}, // {Key:"COUNTRY", Value:"ecuador", deleted:false}, // }, // index: {"MOUNTAIN":0, "COUNTRY":1}, // }, // Steps: { // &pipeline.CommandStep{ // Command: "echo \"hello world\"", // Env: {}, // RemainingFields: {}, // }, // &pipeline.WaitStep{ // Scalar: "wait", // Contents: {}, // }, // &pipeline.CommandStep{ // Command: "echo \"goodbye world\"", // Env: {}, // RemainingFields: {}, // }, // }, // RemainingFields: {}, // } ``` ### 序列化为 YAML 或 JSON ``` aPipeline := `...` p, err := pipeline.Parse(strings.NewReader(aPipeline)) if err != nil { // ... } //... modify the pipeline // Marshal to YAML b, err := yaml.Marshal(p) if err != nil { // ... } // Marshal to JSON b, err := json.Marshal(p) if err != nil { // ... } ``` ## 注意事项 pipeline 对象模型(`Pipeline`、`Steps`、`Plugin` 等)有以下注意事项: - 它是不完整的:可能存在 API 接受但未列出的字段。不要将 Pipeline、CommandStep 等视为如何编写 pipeline 的全面参考指南。 - 它会规范化:反序列化接受多种 step 形式,但重新序列化会生成更规范的输出。反序列化/序列化的往返过程可能会产生不同的输出。 - 它是非规范的:使用此对象模型不能保证 pipeline 会被 pipeline 上传 API 接受。 值得注意的是,此模块定义的大多数 struct 仅包含 agent 理解所需的 pipeline(和 step)元素,并且(在撰写本文时)并不全面。在相关的地方——即存在未包含在 struct 中的更多字段的地方——`RemainingFields` 字段用于将剩余字段捕获为 `map[string]any`。这允许在不丢失信息的情况下加载和修改 pipeline,即使 pipeline 包含 agent 尚未理解的字段。 例如,command step: ``` command: echo "hello world" env: FOO: bar BAZ: qux artifact_paths: - "logs/**/*" - "coverage/**/*" parallelism: 5 ``` 在 go 中将表示为: ``` &pipeline.CommandStep{ Command: `echo "hello world"`, Env: ordered.MapFromItems( ordered.TupleSS("FOO", "bar"), ordered.TupleSS("BAZ", "qux"), ), RemainingFields: map[string]any{ "artifact_paths": []string{"logs/**/*", "coverage/**/*"}, "parallelism": 5, }, } ``` 这个 go struct 将被重新序列化为与原始输入等效的 YAML。 ## Checkout `checkout` 块为 pipeline 或 command step 配置 git checkout 行为。它支持 `skip`、`submodules`、`depth`、`lfs`、`ssh_secret`、`commit_verification` 以及嵌套的 `flags` 映射。`skip`、`lfs` 和 `submodules` 是 `*bool`,因此模型保留了 `true`、`false` 和缺失值之间的区别;`depth`(`*int`)和 `ssh_secret`(`*string`)在显式值和缺失值之间保持相同的区别;`flags` 包含每个阶段的 git 覆盖设置。`commit_verification` 是一个 `string`,支持 `warn` 或 `strict` 值。 最简单的情况是让 step 完全退出 checkout: ``` steps: - command: echo "no git checkout for me" checkout: skip: true ``` step 级别的 `skip: false` 会显式覆盖任何原本会跳过 checkout 的 pipeline 级别或 agent 级别默认值;缺失的 `skip` 则继承适用的默认值。往返过程会保留这种区别,因此 `skip: false` 不会折叠为空映射。 `skip` 映射到 agent 上的 `BUILDKITE_SKIP_CHECKOUT`:`true` 会跳过 checkout 阶段,缺失则交由 agent 默认值处理。`submodules` 遵循相同的三态模式,并映射到 `BUILDKITE_GIT_SUBMODULES`:`true` 和 `false` 会显式设置该环境变量,缺失则交由 agent 默认值处理。 `ssh_secret` 包含 Buildkite Secret 的名称或 ID,该 Secret 包含 agent 用于 git checkout 的 SSH 私钥。agent 负责检索和验证;go-pipeline 仅解析并往返传递该值。 ``` steps: - command: make test checkout: ssh_secret: deploy-key ``` `commit_verification` 启用验证步骤,确保指定的 commit SHA 确实存在于指定的 branch 上。允许的值为 `warn` 或 `strict`。`warn` 会记录警告但允许 checkout 继续;`strict` 如果确定 branch 上不存在该 SHA,则会因错误而导致 checkout 失败。 ``` steps: - command: build.sh checkout: commit_verification: strict ``` `flags` 包含针对 `clone`、`fetch`、`checkout` 和 `clean` 的每阶段 git 调用覆盖设置: ``` steps: - command: build.sh checkout: flags: clone: "--depth 1" fetch: "--prune" checkout: "--force" clean: "-fdx" ``` 每个叶子节点都是 `*string`。省略某个 flag 会保留使用者应用的任何默认值;显式的空字符串(`clone: ""`)会在往返过程中保留,并指示“此阶段没有 flag”。非字符串标量(`clone: 42`、`clone: true`)在解析时会被拒绝,因为该值会作为 flag 文本传递给 git,强制类型转换会悄无声息地产生错误的调用。`flags:` 下的未知键将存入 `RemainingFields`,因此即使 pipeline 使用了此库尚未识别的 flag 名称,它仍然可以正常解析和往返。 pipeline 级别的 `checkout` 为 command step 提供默认值。继承是可选的:使用者将 pipeline 值合并到每个 step 中。合并后,step 的值在每个叶子节点上优先,而 step 未设置的任何内容都从 pipeline 继承,无论是在顶层还是在 `flags` 内部: ``` checkout: skip: true steps: - command: echo "inherits skip: true from the pipeline" - command: echo "explicit override - checkout runs" checkout: skip: false ``` 合并后,第二个 step 的 `skip: false`(step 值优先),而第一个 step 的 `skip: true`(继承而来)。 常规顺序是先执行 `Pipeline.Interpolate`,然后在分发给 agent 之前按 step 进行合并。 `checkout:` 和 `flags:` 都必须是映射。非映射形式(标量,包括 `checkout: true` / `checkout: false` 简写,以及序列)在解析时会被拒绝。退出的写法为 `checkout: { skip: true }`。 如果在 checkout 成为已签名字段之前对 pipeline 进行了签名,那么当该 step 现在携带任何非空的 Checkout 数据(例如,一个仅设置了 `submodules` 的 step)时,验证将会失败。在升级到包含 checkout 的验证器时,请对此类 pipeline 重新签名。 `Checkout.Depth` 是一个 `*int`,其原因与 `Skip` 相同,这可以与任何显式值区分开来,因此可以在 step 级别干净地覆盖继承的 pipeline 级别 depth。后端会验证 `depth >= 1`,但此库不会。 ``` checkout: depth: 10 steps: - command: echo "Shallow depth defaulting to 10 from build level checkout" - command: echo "Deeper shallow at the step level" checkout: depth: 50 ``` `lfs` 是一个 `*bool`,遵循与 `skip` 和 `submodules` 相同的三态模式:`true` 和 `false` 会显式设置行为,缺失值则交由 agent 默认值处理。除非 step 设置了自己的 `lfs`,否则它会继承 pipeline 级别的 `lfs`。 ``` checkout: lfs: true steps: - command: echo "inherits lfs: true from the pipeline" - command: echo "explicit override - no LFS for this step" checkout: lfs: false ``` ## ordered 模块是怎么回事? 在实现 pipeline 模块时,我们遇到了一个问题:在某些情况下,在 buildkite 的 pipeline.yaml 中,map 字段的顺序是有意义的。正因为如此,每当 pipeline 从 YAML 或 JSON 反序列化时,它都需要以一种保留字段顺序的方式进行存储。`ordered` 模块是有序 map 的简单实现。在大多数情况下,当 pipeline 处理用户输入的 map 时,它会在内部将它们存储为 `ordered.Map`。当 pipeline 被重新序列化为 YAML 或 JSON 时,`ordered.Map` 将以正确的顺序进行序列化。 ## 贡献 我们始终欢迎贡献、错误修复、问题和 PR!有关更多详细信息,请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。
标签:Buildkite, EVTX分析, Go, Homebrew安装, Ruby工具, SOC Prime, 开发工具, 日志审计, 流水线