日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

本文將用三種方法來(lái)創(chuàng)建 CI/CD 流水線。Monad 不能對(duì)流水線進(jìn)行靜態(tài)分析,Arrow 語(yǔ)法很難用,我稱之為 Dart(不知道它是否已經(jīng)有名字了)的一種輕量級(jí)的 Arrow 方法可以像 Arrow 一樣進(jìn)行靜態(tài)分析,但語(yǔ)法比 Monad 更簡(jiǎn)單。

我需要構(gòu)建一個(gè)用于創(chuàng)建 CI/CD 流水線的系統(tǒng)。它起初是為了構(gòu)建一個(gè) CI 系統(tǒng),測(cè)試 Github 上的 OCaml 項(xiàng)目(針對(duì)多個(gè)版本的 OCaml 編譯器和多個(gè)操作系統(tǒng),測(cè)試每個(gè)提交)。下面是一個(gè)簡(jiǎn)單的流水線,獲取某個(gè) Git 分支最新的提交,構(gòu)建,并執(zhí)行測(cè)試用例。

【譯者注】CI/CD:持續(xù)集成(Continuous Integration)和持續(xù)部署(Continuous Deployment)簡(jiǎn)稱,指在開(kāi)發(fā)過(guò)程中自動(dòng)執(zhí)行一系列腳本來(lái)減低開(kāi)發(fā)引入 bug 的概率,在新代碼從開(kāi)發(fā)到部署的過(guò)程中,盡量減少人工的介入。

CI/CD流水線創(chuàng)建方法?

 

這里的配色標(biāo)識(shí)是:綠色的方框是已經(jīng)完成,橙色的是正在進(jìn)行,灰色的意味著這一步還不能開(kāi)始。

這里有一個(gè)稍微復(fù)雜點(diǎn)的例子,它還下載了一個(gè) Docker 基礎(chǔ)鏡像,使用兩個(gè)不同版本的 OCaml 編譯器并行構(gòu)建提交,然后測(cè)試得到的鏡像。紅框表示此步驟失?。?/p>CI/CD流水線創(chuàng)建方法?

 

一個(gè)更復(fù)雜的例子是測(cè)試項(xiàng)目本身,然后搜索依賴它的其他項(xiàng)目,并根據(jù)新版本測(cè)試這些項(xiàng)目:

CI/CD流水線創(chuàng)建方法?

 

在這里,圓圈意味著在檢查反向依賴項(xiàng)之前,我們應(yīng)該等待測(cè)試通過(guò)。

我們可以用 YAML 或類似的方法來(lái)描述這些管道,但這將是非常有限的。相反,我決定使用一種特定于領(lǐng)域的嵌入式語(yǔ)言,這樣我們就可以免費(fèi)使用宿主語(yǔ)言的特性(例如字符串操作、變量、函數(shù)、導(dǎo)入、類型檢查等)。

最明顯的方法是使每個(gè)框成為正則函數(shù)。然后上面的第一個(gè)例子可以是(這里,使用 OCaml 語(yǔ)法):

let example1 commit =
  let src = fetch commit in
  let image = build src in
  test image

第二個(gè)可能是:

let example2 commit =
  let src = fetch commit in
  let base = docker_pull "ocaml/opam2" in
  let build ocaml_version =
    let dockerfile = make_dockerfile ~base ~ocaml_version in
    let image = build ~dockerfile src ~label:ocaml_version in
    test image
  in
  build "4.07";
  build "4.08"

第三個(gè)可能是這樣的:

let example3 commit =
  let src = fetch commit in
  let image = build src in
  test image;
  let revdeps = get_revdeps src in
  List.iter example1 revdeps

不過(guò),我們想在語(yǔ)言中添加一些附加功能:

  • 管道步驟應(yīng)盡可能并行運(yùn)行。上面的 example2 函數(shù)將一次完成一個(gè)構(gòu)建。
  • 管道步驟應(yīng)在其輸入更改時(shí)重新計(jì)算。e、 當(dāng)我們作出新的承諾時(shí),我們需要重建。
  • 用戶應(yīng)該能夠查看每個(gè)步驟的進(jìn)度。
  • 用戶應(yīng)該能夠?yàn)槿魏尾襟E觸發(fā)重建。
  • 我們應(yīng)該能夠從代碼中自動(dòng)生成圖表,這樣我們就可以在運(yùn)行管道之前看到它將做什么。
  • 一步的失敗不應(yīng)該使整個(gè)管道停止。

對(duì)于這篇博客文章來(lái)說(shuō),確切的附加功能并不重要,因此為了簡(jiǎn)單起見(jiàn),我將重點(diǎn)放在同時(shí)運(yùn)行步驟上。

Monad 方法

【譯者注】Monad:函子,單子,來(lái)自 Haskell 編程語(yǔ)言,是函數(shù)式編程中,一種定義將函數(shù)(函子)組合起來(lái)的結(jié)構(gòu)方式,它除了返回值以外,還需要一個(gè)上下文。常見(jiàn)的 Monad 有計(jì)算任務(wù),分支任務(wù),或者 I/O 操作。

如果沒(méi)有額外的功能,我們有如下功能:

val fetch : commit -> source
val build : source -> image

您可以將其理解為“build 是一個(gè)獲取源值并返回(Docker)鏡像的函數(shù)”。

這些函數(shù)很容易組合在一起,形成一個(gè)更大的函數(shù)來(lái)獲取提交并構(gòu)建它:

let fab c =
  let src = fetch c in
  build src

CI/CD流水線創(chuàng)建方法?

 

我們還可以將其縮短為 build(fetch c)或 fetch c |>build。OCaml 中的|>(pipe)運(yùn)算符只調(diào)用其右側(cè)的函數(shù),而參數(shù)在其左側(cè)。

為了將這些函數(shù)擴(kuò)展為并發(fā)的,我們可以讓它們返回承諾,例如,

val fetch : commit -> source promise
val build : source -> image promise

但是現(xiàn)在我們無(wú)法使用 let(或|>)輕松組合它們,因?yàn)?fetch 的輸出類型與 build 的輸入不匹配。

但是,我們可以定義一個(gè)類似的操作,let(或>>=)來(lái)處理承諾。它立即返回對(duì)最終結(jié)果的承諾,并在第一個(gè)承諾實(shí)現(xiàn)后調(diào)用 let* 的主體。那么我們有:

let fab c =
  let* src = fetch c in
  build src

換句話說(shuō),通過(guò)在周圍撒上幾個(gè)星號(hào)字符,我們可以將簡(jiǎn)單的舊管道變成一個(gè)新的并發(fā)管道!使用 let* 編寫(xiě) promise returning 函數(shù)的時(shí)間規(guī)則與使用 let 編寫(xiě)常規(guī)函數(shù)的時(shí)間規(guī)則完全相同,因此使用 promise 編寫(xiě)程序與編寫(xiě)常規(guī)程序一樣簡(jiǎn)單。

僅僅使用 let *不會(huì)在我們的管道中添加任何并發(fā)(它只允許它與其他代碼并發(fā)執(zhí)行)。但是我們可以為此定義額外的函數(shù),比如 all 一次計(jì)算一個(gè)列表中的每個(gè)承諾,或者使用 and 運(yùn)算符指示兩個(gè)事物應(yīng)該并行運(yùn)行:

除了處理承諾外,我們還可以為可能返回錯(cuò)誤的函數(shù)(只有在第一個(gè)值成功時(shí)才調(diào)用 let 的主體)或?yàn)閷?shí)時(shí)更新(每次輸入更改時(shí)都調(diào)用主體)或?yàn)樗羞@些東西一起定義 let*。這是單子的基本概念。

這其實(shí)很管用。在 2016 年,我用這種方法做了 DataKitCI,它最初用于 Docker-for-mac 上的 CI 系統(tǒng)。之后,Madhavapeddy 用它創(chuàng)建了 opam-repo-ci,這是 opam-repository 上的 CI 系統(tǒng),OCaml 上主要的軟件倉(cāng)庫(kù)。這將檢查每個(gè)新的 PR 以查看它添加或修改了哪些包,針對(duì)多個(gè) OCaml 編譯器版本和 linux 發(fā)行版(Debian、Ubuntu、Alpine、centos、Fedora 和 OpenSUSE)測(cè)試每個(gè)包,然后根據(jù)更改的包查找所有包的所有版本,并測(cè)試這些版本。

使用 monad 的主要問(wèn)題是我們不能對(duì)管道進(jìn)行靜態(tài)分析。考慮上面的 example2 函數(shù)。在查詢 GitHub 以獲得測(cè)試提交之前,我們無(wú)法運(yùn)行該函數(shù),因此不知道它將做什么。一旦我們有了 commit,我們就可以調(diào)用 example2commit,但是在 fetch 和 docker_pull 操作完成之前,我們無(wú)法計(jì)算 let* 的主體來(lái)找出管道接下來(lái)將做什么。

換言之,我們只能繪制圖表,顯示已經(jīng)執(zhí)行或正在執(zhí)行的管道位,并且必須使用和 * 手動(dòng)指示并發(fā)的機(jī)會(huì)。

Arrow 方法

Arrow 使管道的靜態(tài)分析成為可能。而不是我們的一元函數(shù):

val fetch : commit -> source promise
val build : source -> image promise

我們可以定義箭頭類型:

type ('a, 'b) arrow


val fetch : (commit, source) arrow
val build : (source, image) arrow

a('a,'b)arrow 是一個(gè)接受 a 類型輸入并生成 b 類型結(jié)果的管道。如果我們定義類型('a,'b)arrow='a->'b promise,則這與一元版本相同。但是,我們可以將箭頭類型抽象化,并對(duì)其進(jìn)行擴(kuò)展,以存儲(chǔ)我們需要的任何靜態(tài)信息。例如,我們可以標(biāo)記箭頭:

type ('a, 'b) arrow = {
  f : 'a -> 'b promise;
  label : string;
}

這里,箭頭是一個(gè)記錄。f 是舊的一元函數(shù),label 是“靜態(tài)分析”。

用戶看不到 arrow 類型的內(nèi)部,必須使用 arrow 實(shí)現(xiàn)提供的函數(shù)來(lái)構(gòu)建管道。有三個(gè)基本功能可用:

val arr : ('a -> 'b) -> ('a, 'b) arrow
val ( >>> ) : ('a, 'b) arrow -> ('b, 'c) arrow -> ('a, 'c) arrow
val first : ('a, 'b) arrow -> (('a * 'c), ('b * 'c)) arrow

arr 接受純函數(shù)并給出等價(jià)的箭頭。對(duì)于我們的承諾示例,這意味著箭頭返回已經(jīng)實(shí)現(xiàn)的承諾。>>>把兩個(gè)箭頭連在一起。首先從“a”到“b”取一個(gè)箭頭,改為成對(duì)使用。該對(duì)的第一個(gè)元素將由給定的箭頭處理,第二個(gè)組件將原封不動(dòng)地返回。

我們可以讓這些操作自動(dòng)創(chuàng)建帶有適當(dāng) f 和 label 字段的新箭頭。例如,在 a>>>b 中,結(jié)果 label 字段可以是字符串{a.label}>>{b.label}。這意味著我們可以顯示管道,而不必先運(yùn)行它,如果需要的話,我們可以很容易地用更結(jié)構(gòu)化的內(nèi)容替換 label。

我們的第一個(gè)例子是:

let example1 commit =
  let src = fetch commit in
  let image = build src in
  test image

to

let example1 =
  fetch >>> build >>> test

雖然我們不得不放棄變量名,但這似乎很令人愉快。但事情開(kāi)始變得復(fù)雜,有了更大的例子。例如 2,我們需要定義幾個(gè)標(biāo)準(zhǔn)組合:

(** Process the second component of a tuple, leaving the first unchanged. *)
let second f =
  let swap (x, y) = (y, x) in
  arr swap >>> first f >>> arr swap


(** [f *** g] processes the first component of a pair with [f] and the second
    with [g]. *)
let ( *** ) f g =
  first f >>> second g


(** [f &&& g] processes a single value with [f] and [g] in parallel and
    returns a pair with the results. *)
let ( &&& ) f g =
  arr (fun x -> (x, x)) >>> (f *** g)

Then, example2 changes from:

let example2 commit =
  let src = fetch commit in
  let base = docker_pull "ocaml/opam2" in
  let build ocaml_version =
    let dockerfile = make_dockerfile ~base ~ocaml_version in
    let image = build ~dockerfile src ~label:ocaml_version in
    test image
  in
  build "4.07";
  build "4.08"

to:

let example2 =
  let build ocaml_version =
    first (arr (fun base -> make_dockerfile ~base ~ocaml_version))
    >>> build_with_dockerfile ~label:ocaml_version
    >>> test
  in
  arr (fun c -> ((), c))
  >>> (docker_pull "ocaml/opam2" *** fetch)
  >>> (build "4.07" &&& build "4.08")
  >>> arr (fun ((), ()) -> ())

我們已經(jīng)丟失了大多數(shù)變量名,而不得不使用元組,記住我們的值在哪里。這里有兩個(gè)值并不是很糟糕,但是隨著更多的值被添加并且我們開(kāi)始嵌套元組,它變得非常困難。我們還失去了在 build~dockerfile src 中使用可選標(biāo)記參數(shù)的能力,而是需要使用一個(gè)新操作,該操作接受 dockerfile 和源的元組。

假設(shè)現(xiàn)在運(yùn)行測(cè)試需要從源代碼獲取測(cè)試用例。在原始代碼中,我們只需使用:src 將測(cè)試圖像更改為測(cè)試圖像。在 arrow 版本中,我們需要在生成步驟之前復(fù)制源代碼,使用帶 dockerfile 的 first build_ 運(yùn)行生成,并確保參數(shù)是新測(cè)試使用的正確方法。

Dart 方法

我開(kāi)始懷疑是否有一種更簡(jiǎn)單的方法來(lái)實(shí)現(xiàn)與箭頭相同的靜態(tài)分析,但是沒(méi)有無(wú)點(diǎn)語(yǔ)法,而且似乎有??紤]示例 1 的一元版本。我們有:

val build : source -> image promise
val test : image -> results promise


let example1 commit =
  let* src = fetch commit in
  let* image = build src in
  test image

如果你不知道蒙娜茲的事,你還有別的辦法。您可以定義 build 和 test,將 promises 作為輸入,而不是使用 let* 等待獲取完成,然后使用源調(diào)用 build:

val build : source promise -> image promise
val test : image promise -> results promise

畢竟,fetching 給了你一個(gè)源代碼承諾,你想要一個(gè)圖像承諾,所以這看起來(lái)很自然。我們甚至可以以承諾為例。然后看起來(lái)是這樣的:

let example1 commit =
  let src = fetch commit in
  let image = build src in
  test image

很好,因?yàn)樗臀覀儎傞_(kāi)始的簡(jiǎn)單版本是一樣的。問(wèn)題是效率低下:

  • 我們用承諾的方式調(diào)用 example1(我們還不知道它是什么)。
  • 我們不必等待找出要測(cè)試的提交,而是調(diào)用 fetch,獲取某個(gè)源的承諾。
  • 不需要等待獲取源代碼,我們就調(diào)用 build,獲取圖像的承諾。
  • 不用等待構(gòu)建,我們調(diào)用 test,得到結(jié)果的承諾。

我們立即返回測(cè)試結(jié)果的最終承諾,但我們還沒(méi)有做任何真正的工作。相反,我們建立了一長(zhǎng)串的承諾,浪費(fèi)了記憶。

但是,在這種情況下,我們希望執(zhí)行靜態(tài)分析。i、 我們想在內(nèi)存中建立一些表示流水線的數(shù)據(jù)結(jié)構(gòu)……這正是我們對(duì) monad 的“低效”使用所產(chǎn)生的結(jié)果!

為了使其有用,我們需要基本操作(比如 fetch)來(lái)為靜態(tài)分析提供一些信息(比如標(biāo)簽)。OCaml 的 let 語(yǔ)法沒(méi)有為標(biāo)簽提供明顯的位置,但是我能夠定義一個(gè)運(yùn)算符(let**),該運(yùn)算符返回一個(gè)接受 label 參數(shù)的函數(shù)。它可用于生成如下基本操作:

let fetch commit =
  "fetch" |>
  let** commit = commit in
  (* (standard monadic implementation of fetch goes here) *)

因此,fetch 接受一個(gè)提交的承諾,對(duì)它執(zhí)行一個(gè)單字節(jié)綁定以等待實(shí)際的提交,然后像以前一樣繼續(xù),但它將綁定標(biāo)記為一個(gè) fetch 操作。如果 fetch 包含多個(gè)參數(shù),則可以使用 and* 并行等待所有參數(shù)。

理論上,let**In fetch 的主體可以包含進(jìn)一步的綁定。如果那樣的話,我們?cè)谝婚_(kāi)始就無(wú)法分析整個(gè)管道。但是,只要原語(yǔ)在開(kāi)始時(shí)等待所有輸入,并且不在內(nèi)部進(jìn)行任何綁定,我們就可以靜態(tài)地發(fā)現(xiàn)整個(gè)管道。

我們可以選擇是否將這些綁定操作公開(kāi)給應(yīng)用程序代碼。如果 let*(或 let**)被公開(kāi),那么應(yīng)用程序就可以使用 monad 的所有表達(dá)能力,但是在某些承諾解決之前,我們將無(wú)法顯示整個(gè)管道。如果我們隱藏它們,那么應(yīng)用程序只能生成靜態(tài)管道。

到目前為止,我的方法是使用 let* 作為逃生艙口,這樣就可以建造任何所需的管道,但我后來(lái)用更專業(yè)的操作來(lái)代替它的任何用途。例如,我添加了:

val list_map : ('a t -> 'b t) -> 'a list t -> 'b list t

這將處理運(yùn)行時(shí)才知道的列表中的每個(gè)項(xiàng)。然而,我們?nèi)匀豢梢造o態(tài)地知道我們將應(yīng)用于每個(gè)項(xiàng)的管道,即使我們不知道項(xiàng)本身是什么。list_map 本來(lái)可以使用 let* 實(shí)現(xiàn),但這樣我們就無(wú)法靜態(tài)地看到管道。

下面是另外兩個(gè)使用 dart 方法的示例:

let example2 commit =
  let src = fetch commit in
  let base = docker_pull "ocaml/opam2" in
  let build ocaml_version =
    let dockerfile =
      let+ base = base in
      make_dockerfile ~base ~ocaml_version in
    let image = build ~dockerfile src ~label:ocaml_version in
    test image
  in
  all [
    build "4.07";
    build "4.08"
  ]

與原來(lái)相比,我們有一個(gè) all 來(lái)合并結(jié)果,并且在計(jì)算 dockerfile 時(shí)有一個(gè)額外的 let+base=base。let+ 只是 map 的另一種語(yǔ)法,在這里使用,因?yàn)槲疫x擇不更改 make_dockerfile 的簽名?;蛘?,我們可以讓你的 dockerfile 接受一個(gè)基本圖像的承諾,并在里面做地圖。因?yàn)?map 需要一個(gè)純實(shí)體(make_dockerfile 只生成一個(gè)字符串;沒(méi)有承諾或錯(cuò)誤),所以它不需要在圖表上有自己的框,并且我們不會(huì)因?yàn)樵试S使用它而丟失任何東西。

let example3 commit =
  let src = fetch commit in
  let image = build src in
  let ok = test image in
  let revdeps = get_revdeps src in
  gate revdeps ~on:ok |>
  list_iter ~pp:Fmt.string example1

這顯示了另一個(gè)自定義操作:gate revdeps~on:ok 是一個(gè)承諾,只有在 revdeps 和 ok 都解決后才能解決。這將阻止它在庫(kù)自己的測(cè)試通過(guò)之前測(cè)試庫(kù)的 revdeps,即使如果我們希望它可以并行地這樣做。而對(duì)于 monad,我們必須在需要的地方顯式地啟用并發(fā)(使用和 *),而對(duì)于 dart,我們必須在不需要的地方顯式地禁用并發(fā)(使用 gate)。

我還添加了一個(gè) list-iter 便利函數(shù),并為它提供了一個(gè)漂亮的 printer 參數(shù),這樣一旦知道列表輸入,我們就可以在圖表中標(biāo)記案例。

最后,雖然我說(shuō)過(guò)不能在原語(yǔ)中使用 let*,但仍然可以使用其他一些 monad(它不會(huì)生成圖)。實(shí)際上,在實(shí)際系統(tǒng)中,我對(duì)原語(yǔ)使用了一個(gè)單獨(dú)的 let>操作符。這就要求主體使用底層 promise 庫(kù)提供的非圖生成承諾,因此不能在原語(yǔ)的主體中使用 let*(或 let>)。

和 Arrow 進(jìn)行比較

給定一個(gè)“dart”,您可以通過(guò)定義例如。

type ('a, 'b) arrow = 'a promise -> 'b promise

那么 arr 就是 map,f>>>g 就是有趣的 x->g(fx)。第一個(gè)也可以很容易地定義,假設(shè)你有某種函數(shù)來(lái)并行地做兩件事(比如上面的和我們的)。

因此,dart API(即使有 let*hidden)仍然足以表示任何可以使用箭頭 API 表示的管道。

Haskell 箭頭教程 使用一個(gè)箭頭是有狀態(tài)函數(shù)的示例。例如,有一個(gè) total 箭頭,它返回它的輸入和以前調(diào)用它的每個(gè)輸入的總和。e、 g. 用輸入 1 2 3 調(diào)用三次,產(chǎn)生輸出 1 3 6。對(duì)輸入序列運(yùn)行管道將返回輸出序列。

本教程使用 total 定義 mean1 函數(shù),如下所示:

mean1 = (total &&& (arr (const 1) >>> total)) >>> arr (uncurry (/))

因此,此管道復(fù)制每個(gè)輸入編號(hào),將第二個(gè)編號(hào)替換為 1,將兩個(gè)流相加,然后用其比率替換每對(duì)。每次將另一個(gè)數(shù)字放入管道時(shí),都會(huì)得到迄今為止輸入的所有值的平均值。

使用 dart 樣式的等效代碼是(OCaml 使用 /。對(duì)于浮點(diǎn)除法):

let mean values =
  let t = total values in
  let n = total (const 1.0) in
  map (uncurry (/.)) (pair t n)

這對(duì)我來(lái)說(shuō)更容易理解。通過(guò)定義標(biāo)準(zhǔn)運(yùn)算符 let+(對(duì)于 map)和 +(對(duì)于 pair),我們可以稍微簡(jiǎn)化代碼:

let (let+) x f = map f x
let (and+) = pair


let mean values =
  let+ t = total values
  and+ n = total (const 1.0) in
  t /. n

無(wú)論如何,這不是一個(gè)很好的箭頭示例,因?yàn)槲覀儾皇褂靡粋€(gè)狀態(tài)函數(shù)的輸出作為另一個(gè)狀態(tài)函數(shù)的輸入,所以這實(shí)際上只是一個(gè)簡(jiǎn)單的 Applicative.

不過(guò),我們可以很容易地用另一個(gè)有狀態(tài)函數(shù)擴(kuò)展示例管道,也許可以添加一些平滑處理。這看起來(lái)像箭頭符號(hào)中的 mean1>>>平滑,省道符號(hào)中的值|>平均值|>平滑(或平滑(平均值))。

注意:Haskell 還有一個(gè) Arrows 語(yǔ)法擴(kuò)展,它允許 Haskell 代碼編寫(xiě)為:

mean2 = proc value -> do
    t <- total -< value
    n <- total -< 1
    returnA -< t / n

這更像是飛鏢符號(hào)。

更多示例

我在 ocurrent/ocurrent 上建立了一個(gè)使用這些思想的稍微擴(kuò)展版本的庫(kù)。子目錄 lib_term 是與這篇博客文章相關(guān)的部分,在 TERM 中描述了各種組合詞。

其他目錄處理更具體的細(xì)節(jié),例如與 Lwt promise 庫(kù)的集成,提供管理 web UI 或 Cap’n Proto RPC 接口,以及帶有用于使用 Git、GitHub、Docker 和 Slack 的原語(yǔ)的插件。

OCaml Docker 基礎(chǔ)鏡像構(gòu)建

ocurrent/docker-base-images 包含一個(gè)管道,用于為各種 Linux 發(fā)行版、CPU 架構(gòu)、OCaml 編譯器版本和配置選項(xiàng)構(gòu)建 OCaml 的 Docker 基本映像。例如,要在 Debian 10 上測(cè)試 OCAML4.09,可以執(zhí)行以下操作:

$ docker run --rm -it ocurrent/opam:debian-10-ocaml-4.09


:~$ ocamlopt --version
4.09.0


:~$ opam depext -i utop
[...]


:~$ utop
----+-------------------------------------------------------------+------------------
    | Welcome to utop version 2.4.2 (using OCaml version 4.09.0)! |
    +-------------------------------------------------------------+


Type #utop_help for help about using utop.


-( 11:50:06 )-< command 0 >-------------------------------------------{ counter: 0 }-
utop #

以下是管道的外觀(單擊可查看完整尺寸)

CI/CD流水線創(chuàng)建方法?

 

它每周提取 opam 存儲(chǔ)庫(kù)的最新 Git 提交,然后為每個(gè)發(fā)行版構(gòu)建包含該內(nèi)容的基本映像和 opam 包管理器,然后為每個(gè)受支持的編譯器變體構(gòu)建一個(gè)映像。許多圖片是建立在多個(gè)架構(gòu)(amd64、arm32、arm64 和 ppc64)上的,并被推到 Docker Hub 的一個(gè)暫存區(qū)。然后,管道將所有散列組合起來(lái),將一個(gè)多架構(gòu)清單推送到 Docker Hub。還有一些別名(例如,debian 表示 debian-10-ocaml-4.09)。最后,如果有任何問(wèn)題,則管道會(huì)將錯(cuò)誤發(fā)送到松弛通道。

您可能想知道,我們是否真的需要一個(gè)管道來(lái)實(shí)現(xiàn)這一點(diǎn),而不是從 cron 作業(yè)運(yùn)行一個(gè)簡(jiǎn)單的腳本。但是擁有一個(gè)管道可以讓我們?cè)谶\(yùn)行它之前看到管道將要做什么,觀察管道的進(jìn)度,單獨(dú)重新啟動(dòng)失敗的作業(yè),等等,幾乎與我們編寫(xiě)的代碼相同。

如果你想看完成的流水線,可以閱讀 pipeline.ml。

OCaml CI

ocurrent/ocaml-ci 是一個(gè)用于測(cè)試 OCaml 項(xiàng)目的(實(shí)驗(yàn)性的)GitHub 應(yīng)用程序。管道獲取應(yīng)用程序的安裝列表,獲取每個(gè)安裝的已配置存儲(chǔ)庫(kù),獲取每個(gè)存儲(chǔ)庫(kù)的分支和 PRs,然后針對(duì)多個(gè) Linux 發(fā)行版和 OCaml 編譯器版本測(cè)試每個(gè)存儲(chǔ)庫(kù)的頭部。如果項(xiàng)目使用 ocamlformat,它還會(huì)檢查提交的格式是否與 ocamlformat 的格式完全相同。

CI/CD流水線創(chuàng)建方法?

 

結(jié)果作為提交狀態(tài)被推回到 GitHub,并記錄在 web 和 tty ui 的本地索引中。這里有很多紅色,主要是因?yàn)槿绻粋€(gè)項(xiàng)目不支持特定版本的 OCaml,那么構(gòu)建會(huì)被標(biāo)記為失敗,并在管道中顯示為紅色,盡管在生成 GitHub 狀態(tài)報(bào)告時(shí)會(huì)過(guò)濾掉這些失敗。我們可能需要一個(gè)新的顏色跳過(guò)階段。

結(jié)論

編寫(xiě) CI/CD 管道很方便,就好像它們是一次連續(xù)運(yùn)行這些步驟并始終成功的單點(diǎn)腳本一樣,然后只要稍作更改,管道就會(huì)在輸入更改時(shí)運(yùn)行這些步驟,同時(shí)提供日志記錄、錯(cuò)誤報(bào)告、取消和重建支持。

使用 monad 可以很容易地將任何程序轉(zhuǎn)換為具有這些特性的程序,但是,與常規(guī)程序一樣,在運(yùn)行某些數(shù)據(jù)之前,我們不知道該程序?qū)⑷绾翁幚磉@些數(shù)據(jù)。特別是,我們只能自動(dòng)生成顯示已經(jīng)開(kāi)始的步驟的圖表。

傳統(tǒng)的靜態(tài)分析方法是使用箭頭。這比單元格稍微有限,因?yàn)榱魉€的結(jié)構(gòu)不能根據(jù)輸入數(shù)據(jù)而改變,盡管我們可以增加有限的靈活性,例如可選的步驟或兩個(gè)分支之間的選擇。但是,使用箭頭符號(hào)編寫(xiě)管道是很困難的,因?yàn)槲覀儽仨毷褂脽o(wú)點(diǎn)樣式(沒(méi)有變量)編程。

通過(guò)以一種不尋常的方式使用 monad(這里稱為“dart”),我們可以獲得靜態(tài)分析的相同好處。我們的函數(shù)不是接受純值并返回包裝值的函數(shù),而是接受并返回包裝值。這導(dǎo)致語(yǔ)法看起來(lái)與普通編程相同,但允許靜態(tài)分析(代價(jià)是無(wú)法直接操作包裝的值)。

如果我們隱藏(或不使用)monad 的 let*(bind)函數(shù),那么我們創(chuàng)建的管道總是可以靜態(tài)地確定的。如果我們使用綁定,那么管道中會(huì)有隨著管道運(yùn)行而擴(kuò)展到更多管道階段的孔。

基本步驟可以通過(guò)使用單個(gè)“標(biāo)簽綁定”創(chuàng)建,其中標(biāo)簽為原子組件提供靜態(tài)分析。

我以前從未見(jiàn)過(guò)使用過(guò)這種模式(或者在 arrow 文檔中提到過(guò)),它似乎提供了與 arrow 完全相同的好處,而且難度要小得多。如果這個(gè)名字是真的,告訴我!

這項(xiàng)工作由 OCaml 實(shí)驗(yàn)室資助。

原文鏈接:

https://roscidus.com/blog/blog/2019/11/14/cicd-pipelines/

分享到:
標(biāo)簽:CI CD
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定