Skip to content

任務與步驟 ​

這是 Walis 最值得先弄懂的一部分。其他東西——觸發、執行器、通知——都是繞著這個模型的配管。

三個名詞 ​

是什麼存在哪
任務設定:一段腳本、在哪跑、什麼時候跑、誰可以跑。Jobs 的一列。
建置任務的一次執行,有編號、狀態與日誌。Builds 的一列。
步驟建置裡的一個 shell,各自有狀態、輸出、結束碼與耗時。BuildSteps 的數列。

重點在後半:任務本身不存步驟。步驟是建置開始時推導出來的:

  • 直接寫腳本的任務 → 一個步驟
  • 用動作流程組成的任務 → 一個動作一個步驟

所以改任務永遠不會改寫歷史;當初把整套步驟模型加進 Walis 時,也不需要搬遷任何過去的建置—— 任務上根本沒有東西要搬。

腳本就是介面 ​

Walis 執行的是 shell,只有 shell。流程模式是腳本的編輯器,不是執行環境: 儲存任務時,流程會被編譯成一段純 shell 存進 Jobs.Command。

這件事的幾個後果值得講明白:

  • 你看得到將會跑什麼。 切到腳本模式,整段就在那裡。
  • 執行器不需要知道動作的存在。 遠端執行器收到的是 shell,它從沒聽過動作、外掛或 stage。
  • 外掛回傳的是文字,不是行為。 動作外掛產生一段 shell,你可以在安裝前先讀過。
  • 編譯只有單向無損。 流程 → 腳本會保留編譯結果;腳本 → 流程會還原步驟並丟掉手動修改。 一旦你手工調過腳本,就待在腳本模式。

每個步驟都是全新的 shell ​

步驟共用工作目錄,不共用環境變數。要把值傳給後面的步驟,往 Walis 指定的檔案追加:

bash
echo "BUILT_VERSION=1.2.3" >> "$WALIS_ENV_FILE"

下一步就讀得到 $BUILT_VERSION。這跟其他 CI 的語意一致,也避免某一步默默依賴前一步的 shell 狀態——那種「把兩個步驟對調就壞掉,而且沒人看得出為什麼」的毛病。

建置拿到的環境是一份白名單,不是伺服器環境過濾後的副本:只繼承 PATH、USER、SHELL、 語系、暫存目錄與 TLS 憑證那幾個變數,其餘一概沒有。控制端的連線字串與服務機密根本不在裡面, 也就無從外洩。

把值傳下去的兩條路 ​

是什麼適合
$WALIS_ENV_FILE追加 KEY=VALUE,後面的步驟直接讀環境變數。無型別的中間值。哪裡都不留紀錄。
$WALIS_OUTPUT_FILE寫 name=value(一行一個)或一個 JSON 物件,這是這一步的輸出。值得留下來的值:會存進建置紀錄、在日誌的步驟區塊看得到,而且能被具名引用。

引用的寫法是在後面步驟的參數值裡放 ${{ steps.識別字.名稱 }},識別字在那一步的設定裡填。 儲存任務時引用會編譯成一個普通的 shell 變數,執行時把值以環境變數注入—— 值從頭到尾不會被拼進腳本文字,所以沒有東西可以注入。遠端執行器不需要懂這套語法, 它跑的是同一份編譯結果。

只能引用依賴鏈上游的步驟(含遞移)。跟你同時在跑的、或排在你後面的步驟引用不到—— 那是圖在告訴你,那個值當下可能還不存在。上限:輸出 100 個、每個值 8 KB、整個輸出檔 64 KB。

內建動作設定輸出就是寫那個檔案的捷徑。腳本模式則直接讀 $WALIS_STEP_<識別字>_<名稱>。

依資料決定跑不跑 ​

除了執行條件之外,步驟還可以帶一個執行條件(運算式), 執行時拿前面步驟的輸出與環境變數求值:

steps.check.changed == 'true' && env.BRANCH != 'main'
matches(steps.api.status, '^2..$') || empty(steps.api.body)

條件不成立是把這一步標成略過,不是失敗,所以下游仍然照自己的規則判斷。 兩條分支各設互斥的條件、都依賴同一個上游,就是 if/else 的寫法。

語法刻意很小:字串常值、steps.<識別字>.<名稱>、env.<名稱>、==、!=、&&、||、!、括號, 以及 contains、startsWith、endsWith、matches、empty 五個函式。沒有算術、沒有指派。 儲存任務時會檢查語法,並確認引用指向的是前置步驟;執行時語法錯誤會讓那一步失敗。

執行條件與這個運算式兩個都要成立,這一步才會跑。

每個步驟帶的開關 ​

流程裡的每個步驟都有五個開關,跟它用哪個動作無關:

開關作用
啟用這個步驟關掉就略過,不會讓建置失敗。原本依賴它的步驟改成依賴它的上游。
失敗仍繼續結束碼非零也不算建置失敗。
依賴步驟哪幾步跑完才輪到這一步。流程之所以是一張圖,就是靠這個。
識別字一個短名字(小寫、流程內唯一),後面的步驟才引用得到這一步的輸出。
執行條件「前面都成功才執行」(預設)、「總是執行」、「只在前面失敗時執行」——判斷的對象是這一步的依賴,不是整場建置。
執行條件(運算式)執行時拿前面步驟的輸出與環境變數求值。不成立是略過,不是失敗。
重試次數第一次之外再試 0–10 次。

執行條件就是 Jenkins 用 post { always / failure } 表達的東西,而它改變了「失敗」的意思:

步驟失敗不再直接中止建置。後面標記「總是」或「只在前面失敗時」的步驟照樣會跑—— 收尾、拆除、發通知——標記「成功時」的則標成 Skipped。建置結果仍然是失敗。

真正會讓建置中途停下來的只有任務逾時,其餘都會把整個計畫跑完。

重試每次都給一個全新的 shell,前幾次的輸出接在這次上面,所以看得出來是第三次才過的。 重試不會延長任務逾時——每次拿到的是剩下的時間。

腳本模式沒有逐步驟的開關,因為一段編譯好的腳本沒有地方放它們。Jenkins 匯入器遇到表達不了的 post 區塊時會提醒你這件事。

流程是一張圖,不是一串清單 ​

步驟宣告的是自己依賴誰,其他都是這件事的推論:

  • 一個步驟等它依賴的步驟全部結束才開始。
  • 沒有依賴的步驟是起點,同時開始。
  • 彼此不相依的分支就同時跑——每個建置最多 8 步同時。

所以「平行」不是一個要打開的開關,而是「兩步之間沒有依賴」的自然結果。流程模式有清單與 圖兩種檢視:在圖上從節點右側的圓點拖到另一個節點就是加一條依賴,點連線就拿掉。 位置是自動排版、不存檔,所以拖動節點不會改到任何東西。

這個功能出現之前建立的任務照樣能跑。沒有記錄依賴的流程會從舊的「順序+平行」安排推導出來, 語意完全一樣。第一次動到依賴時——勾一個框或在圖上拉一條線——編輯器就把整個流程轉成明確的圖, 跟著下一次儲存一起送出。沒有要跑的轉換步驟,也不用確認什麼。

失敗在圖上會怎樣 ​

執行條件判斷的對象是這一步自己的依賴,不是整場建置:

  • 前面都成功才執行——它依賴的每一步都成功。失敗但設了「失敗仍繼續」的算成功。
  • 只在前面失敗時執行——上游有失敗,包含因為更前面失敗而被略過的步驟。
  • 總是執行——不管怎樣都跑。

所以一條分支失敗,不會動到不依賴它的分支。分支匯合的地方會被標成略過,並把失敗往下游傳—— 這就是為什麼接在匯合點後面的收尾或通知步驟照樣會跑。

同時執行的步驟各自有自己的 $WALIS_ENV_FILE,共用會互相蓋掉。 有多步同時在跑的期間彙總日誌不即時更新,各步驟的輸出照常串流。

建置參數 ​

一個任務最多宣告 50 個參數,型別為 string、text、choice、boolean 或 password。 手動執行時會先開表單;排程與推送用預設值。MCP 的 trigger_build 也收參數。

值以環境變數送進去,腳本用 $RELEASE_TAG 讀。有兩條規則讓它不會變成注入漏洞:

  • 名稱不可以 WALIS_ 開頭。 那個前綴是系統的。
  • 不認識的名稱是拒絕,不是忽略。 傳一個任務沒宣告過的參數會讓觸發失敗, 所以參數不能拿來設任意環境變數。選單型別的值也必須在宣告的選項裡。

password 型別從頭到尾另外處理:值存在一個 API 永遠不會序列化的欄位、列入日誌遮蔽清單、 介面顯示成 ********。非機密的值在建置上看得到——回頭讀舊建置時通常正需要這個。

同一時間只跑一筆 ​

關掉「允許同時建置」就是 Jenkins 的 disableConcurrentBuilds()。第二筆建置不會被拒絕, 而是留在佇列等第一筆結束。

這道關卡擋在該擋的地方:跟「把建置從 Queued 改成 Running」同一個交易裡, 所以兩個節點搶同一個任務不可能都贏。本機執行器搶輸的話,過幾秒會把建置重新排回佇列,不會弄丟。

下游任務 ​

一個任務最多列 20 個完成後要觸發的任務,時機可選成功時、失敗時或總是。 對象以名稱指定而不是識別碼,所以匯出到另一台機器仍然對得上。

下游建置會拿到 WALIS_UPSTREAM_JOB、WALIS_UPSTREAM_BUILD 與 WALIS_UPSTREAM_STATUS, 腳本可以知道自己是被誰叫起來的。

有兩個刻意的限制:

  • 鏈長到 10 就停。 A → B → A 不會無限跑,跑十層就停。
  • 用上游任務的執行角色當門檻。 自動觸發背後沒有互動式身分,所以 Walis 拿上游任務的 執行角色去檢查下游。一個 operator 就能跑的任務,不會變成通往管理員限定部署任務的側門。

跟 Jenkins 的 build job: 不一樣,Walis 不等下游的結果。上游那筆建置已經結束了, 下游是另一筆有自己編號的建置。

矩陣建置 ​

給任務一個矩陣,一次觸發就會為每個組合各跑一次:

json
{ "NODE": ["20", "22"], "OS": ["mac", "linux"] }

每個建置會拿到對應的環境變數,並帶上像 NODE=20, OS=mac 的標籤。 上限 50 組——四個軸各五個值就是 625 次建置,通常不是本意。

快取 ​

列出要在建置之間保留的路徑(相對於工作目錄)。成功的建置會保存下來,下一次還原。 順序很重要:Walis 先清空工作目錄再還原——反過來會把剛還原的東西清掉。絕對路徑與 .. 一律忽略。

快取與產出收集都跑在 API 程序裡而不是建置裡面,所以兩者一律不跟符號連結。 不然 ln -s <資料目錄> out 就能把產出收集變成把資料庫複製出去的手段。

逾時 ​

任務逾時是整場建置的預算,不是每個步驟各一份。每個步驟拿到的是剩下的時間; 開始時已經沒時間的步驟會直接以說明訊息判為失敗,而不是啟動後寫到一半被砍掉。

從 Jenkins 匯入 ​

已經有 Jenkinsfile 就別重打了——匯入器與完整對應表在這裡。

以 MIT 授權釋出。