CI/CD流水线创建方法?

本文将用三种方法来创建CI/CD流水线 。 Monad不能对流水线进行静态分析 , Arrow语法很难用 , 我称之为Dart(不知道它是否已经有名字了)的一种轻量级的Arrow方法可以像Arrow一样进行静态分析 , 但语法比Monad更简单 。
我需要构建一个用于创建CI/CD流水线的系统 。 它起初是为了构建一个CI系统 , 测试GitHub上的OCaml项目(针对多个版本的OCaml编译器和多个操作系统 , 测试每个提交) 。 下面是一个简单的流水线 , 获取某个Git分支最新的提交 , 构建 , 并执行测试用例 。
【译者注】CI/CD:持续集成(ContinuousIntegration)和持续部署(ContinuousDeployment)简称 , 指在开发过程中自动执行一系列脚本来减低开发引入bug的概率 , 在新代码从开发到部署的过程中 , 尽量减少人工的介入 。

CI/CD流水线创建方法?
文章图片
这里的配色标识是:绿色的方框是已经完成 , 橙色的是正在进行 , 灰色的意味着这一步还不能开始 。
这里有一个稍微复杂点的例子 , 它还下载了一个Docker基础镜像 , 使用两个不同版本的OCaml编译器并行构建提交 , 然后测试得到的镜像 。 红框表示此步骤失败:

CI/CD流水线创建方法?
文章图片
一个更复杂的例子是测试项目本身 , 然后搜索依赖它的其他项目 , 并根据新版本测试这些项目:

CI/CD流水线创建方法?
文章图片
在这里 , 圆圈意味着在检查反向依赖项之前 , 我们应该等待测试通过 。
我们可以用YAML或类似的方法来描述这些管道 , 但这将是非常有限的 。 相反 , 我决定使用一种特定于领域的嵌入式语言 , 这样我们就可以免费使用宿主语言的特性(例如字符串操作、变量、函数、导入、类型检查等) 。
最明显的方法是使每个框成为正则函数 。 然后上面的第一个例子可以是(这里 , 使用OCaml语法):
letexample1commit=let src=https://pcff.toutiao.jxnews.com.cn/p/20210218/fetchcommitinletimage=build srcintestimage第二个可能是:
letexample2commit=let src=https://pcff.toutiao.jxnews.com.cn/p/20210218/fetchcommitinletbase=docker_pull''ocaml/opam2''inletbuildocaml_version=letdockerfile=make_dockerfile~base~ocaml_versioninletimage=build~dockerfile src~label:ocaml_versionintestimageinbuild''4.07'';build''4.08''第三个可能是这样的:
letexample3commit=let src=https://pcff.toutiao.jxnews.com.cn/p/20210218/fetchcommitinletimage=build srcintestimage;letrevdeps=get_revdeps srcinList.iterexample1revdeps不过 , 我们想在语言中添加一些附加功能:
管道步骤应尽可能并行运行 。 上面的example2函数将一次完成一个构建 。 管道步骤应在其输入更改时重新计算 。 e、当我们作出新的承诺时 , 我们需要重建 。 用户应该能够查看每个步骤的进度 。 用户应该能够为任何步骤触发重建 。 我们应该能够从代码中自动生成图表 , 这样我们就可以在运行管道之前看到它将做什么 。 一步的失败不应该使整个管道停止 。对于这篇博客文章来说 , 确切的附加功能并不重要 , 因此为了简单起见 , 我将重点放在同时运行步骤上 。
Monad方法
【译者注】Monad:函子 , 单子 , 来自Haskell编程语言 , 是函数式编程中 , 一种定义将函数(函子)组合起来的结构方式 , 它除了返回值以外 , 还需要一个上下文 。 常见的Monad有计算任务 , 分支任务 , 或者I/O操作 。
如果没有额外的功能 , 我们有如下功能:
valfetch:commit->sourcevalbuild:source->image您可以将其理解为“build是一个获取源值并返回(Docker)镜像的函数” 。
这些函数很容易组合在一起 , 形成一个更大的函数来获取提交并构建它:
letfabc=let src=https://pcff.toutiao.jxnews.com.cn/p/20210218/fetchcinbuild src
CI/CD流水线创建方法?
文章图片
我们还可以将其缩短为build(fetchc)或fetchc|>build 。 OCaml中的|>(pipe)运算符只调用其右侧的函数 , 而参数在其左侧 。
为了将这些函数扩展为并发的 , 我们可以让它们返回承诺 , 例如 ,
valfetch:commit->sourcepromisevalbuild:source->imagepromise但是现在我们无法使用let(或|>)轻松组合它们 , 因为fetch的输出类型与build的输入不匹配 。
但是 , 我们可以定义一个类似的操作 , let(或>>=)来处理承诺 。 它立即返回对最终结果的承诺 , 并在第一个承诺实现后调用let*的主体 。 那么我们有:
letfabc=let* src=https://pcff.toutiao.jxnews.com.cn/p/20210218/fetchcinbuild src换句话说 , 通过在周围撒上几个星号字符 , 我们可以将简单的旧管道变成一个新的并发管道!使用let*编写promisereturning函数的时间规则与使用let编写常规函数的时间规则完全相同 , 因此使用promise编写程序与编写常规程序一样简单 。