Jenkins pipeline語法詳解

2020-08-13 21:06:29

一、引言

  Jenkins 2.0的到來,pipline進入了視野,jenkins2.0的核心特性. 也是最適合持續交付的feature。

  簡單的來說,就是把Jenkins1.0版本中,Project中的相關設定資訊,如SVN/Git的設定,Parameter的設定等都變成Code,即Pipeline as Code。

  隨着pipeline交付流水線在團隊中的推廣,使用pipeline指令碼的job也迅速增加。

  優勢:

    •     通過寫程式碼的形式設定Project,且Jenkins中內建了常用的steps。
    •     實現了構建步驟程式碼化、構建過程檢視化。

二、語法簡介

  Pipeline最基本的部分是「step」。基本上,step告訴Jenkins 要做什麼,並且作爲Declarative Pipeline和Scripted Pipeline語法的基本構建塊。 
  Pipeline支援兩種語法:Declarative Pipeline(在Pipeline 2.5中引入,結構化方式)和Scripted Pipeline,兩者都支援建立連續輸送的Pipeline。

選擇Declarative Pipeline還是Scripted Pipeline

  最開始的Pipeline plugin,支援的只有一種指令碼型別,就是Scripted Pipeline;
  Declarative Pipeline爲Pipeline plugin在2.5版本之後新增的一種指令碼型別,與原先的Scripted Pipeline一樣,都可以用來編寫指令碼。

  相關資料:

  https://stackoverflow.com/questions/43484979/jenkins-scripted-pipeline-or-declarative-pipeline
  http://jenkins-ci.361315.n4.nabble.com/Declarative-pipelines-vs-scripted-td4891792.html

  從檢索的資料來看,Declarative Pipeline 是後續Open Blue Ocean所支援的型別。相對而言,Declarative Pipeline比較簡單,Declarative Pipeline中,也是可以內嵌Scripted Pipeline程式碼的。

  爲與BlueOcean指令碼編輯器相容,通常建議使用Declarative Pipeline的方式進行編寫,從jenkins社羣的動向來看,很明顯這種語法結構也會是未來的趨勢。

三、Declarative Pipeline

Declarative Pipeline是Jenkins Pipeline 的一個相對較新的補充, 它在Pipeline子系統之上提出了一種更爲簡化和有意義的語法。
所有有效的Declarative Pipeline必須包含在一個pipeline塊內,例如:
pipeline { /* insert Declarative Pipeline here */ }
Declarative Pipeline中的基本語句和表達式遵循與Groovy語法相同的規則 ,但有以下例外:

  •   a.Pipeline的頂層必須是塊,具體來說是:pipeline { }
  •   b.沒有分號作爲語句分隔符。每個宣告必須在自己的一行
  •   c.塊只能包含Sections, Directives, Steps或賦值語句。
  •   d.屬性參照語句被視爲無參方法呼叫。所以例如,輸入被視爲input()

1.Sections(章節)

  Declarative Pipeline裡的Sections通常包含一個或多個Directives或 Steps

agent

  agent部分指定整個Pipeline或特定階段將在Jenkins環境中執行的位置,具體取決於該agent 部分的放置位置。該部分必須在pipeline塊內的頂層定義 ,但stage級使用是可選的。

  

  爲了支援Pipeline可能擁有的各種用例,該agent部分支援幾種不同類型的參數。這些參數可以應用於pipeline塊的頂層,也可以應用在每個stage指令內。

參數

  any

    在任何可用的agent 上執行Pipeline或stage。例如:agent any
  none
    當在pipeline塊的頂層使用none時,將不會爲整個Pipeline執行分配全域性agent ,每個stage部分將需要包含其自己的agent部分。
  label
    使用提供的label標籤,在Jenkins環境中可用的代理上執行Pipeline或stage。例如:agent { label 'my-defined-label' }
  node
    agent { node { label 'labelName' } },等同於 agent { label 'labelName' },但node允許其他選項(如customWorkspace)。
  docker
    定義此參數時,執行Pipeline或stage時會動態供應一個docker節點去接受Docker-based的Pipelines。 docker還可以接受一個args,直接傳遞給docker run呼叫。例如:agent { docker 'maven:3-alpine' }或

1

2

3

4

5

6

7

8

docker

agent {

    docker {

        image 'maven:3-alpine'

        label 'my-defined-label'

        args  '-v /tmp:/tmp'

    }

}

  dockerfile

    使用從Dockerfile源儲存庫中包含的容器來構建執行Pipeline或stage 。爲了使用此選項,Jenkinsfile必須從Multibranch Pipeline或「Pipeline from SCM"載入。
    預設是在Dockerfile源庫的根目錄:agent { dockerfile true }。如果Dockerfile需在另一個目錄中建立,請使用以下dir選項:agent { dockerfile { dir 'someSubDir' } }。您可以通過docker build ...使用additionalBuildArgs選項,如agent {     dockerfile { additionalBuildArgs '--build-arg foo=bar' } }。

 

參數
any
  在任何可用的agent 上執行Pipeline或stage。例如:agent any
none
  當在pipeline塊的頂層使用none時,將不會爲整個Pipeline執行分配全域性agent ,每個stage部分將需要包含其自己的agent部分。
label
  使用提供的label標籤,在Jenkins環境中可用的代理上執行Pipeline或stage。例如:agent { label 'my-defined-label' }
node
  agent { node { label 'labelName' } },等同於 agent { label 'labelName' },但node允許其他選項(如customWorkspace)。
docker
  定義此參數時,執行Pipeline或stage時會動態供應一個docker節點去接受Docker-based的Pipelines。 docker還可以接受一個args,直接傳遞給docker run呼叫。例如:agent { docker 'maven:3-alpine' }或

docker
agent {
    docker {
        image 'maven:3-alpine'
        label 'my-defined-label'
        args  '-v /tmp:/tmp'
    }
}

dockerfile
使用從Dockerfile源儲存庫中包含的容器來構建執行Pipeline或stage 。爲了使用此選項,Jenkinsfile必須從Multibranch Pipeline或「Pipeline from SCM"載入。
預設是在Dockerfile源庫的根目錄:agent { dockerfile true }。如果Dockerfile需在另一個目錄中建立,請使用以下dir選項:agent { dockerfile { dir 'someSubDir' } }。您可以通過docker build ...使用additionalBuildArgs選項,如agent { dockerfile { additionalBuildArgs '--build-arg foo=bar' } }。

常用選項

這些是可以應用於兩個或多個agent的選項。除非明確定義,否則不需要。
  label
    一個字串。標記在哪裏執行pipeline或stage
    此選項適用於node,docker和dockerfile,並且 node是必需的。
  customWorkspace
    一個字串。自定義執行的工作空間內。它可以是相對路徑,在這種情況下,自定義工作區將位於節點上的工作空間根目錄下,也可以是絕對路徑。例如:

1

2

3

4

5

6

agent {

    node {

        label 'my-defined-label'

        customWorkspace '/some/other/path'

    }

}

  reuseNode
    一個布爾值,預設爲false。如果爲true,則在同一工作空間中。
    此選項適用於docker和dockerfile,並且僅在 individual stage中使用agent纔有效。

1

2

3

4

5

6

7

8

9

10

11

pipeline {

    //Execute all the steps defined in this Pipeline within a newly created container of the given name and tag (maven:3-alpine).

    agent { docker 'maven:3-alpine' }

    stages {

        stage('Example Build') {

            steps {

                sh 'mvn -B clean verify'

            }

        }

    }

}

  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

pipeline {

    agent none

    stages {

        stage('Example Build') {

            agent { docker 'maven:3-alpine' }

            steps {

                echo 'Hello, Maven'

                sh 'mvn --version'

            }

        }

        stage('Example Test') {

            agent { docker 'openjdk:8-jre' }

            steps {

                echo 'Hello, JDK'

                sh 'java -version'

            }

        }

    }

}

  

post

  定義Pipeline或stage執行結束時的操作。post-condition塊支援post部件:always,changed,failure,success,unstable,和aborted。這些塊允許在Pipeline或stage執行結束時執行步驟,具體取決於Pipeline的狀態。

  

  conditions項:
  always
    執行,無論Pipeline執行的完成狀態如何。
  changed
    只有當前Pipeline執行的狀態與先前完成的Pipeline的狀態不同時,才能 纔能執行。
  failure
    僅噹噹前Pipeline處於「失敗」狀態時才執行,通常在Web UI中用紅色指示表示。
  success
    僅噹噹前Pipeline具有「成功」狀態時才執行,通常在具有藍色或綠色指示的Web UI中表示。
  unstable
    只有當前Pipeline具有「不穩定」狀態,通常由測試失敗,程式碼違例等引起,才能 纔能執行。通常在具有黃色指示的Web UI中表示。
  aborted
    只有當前Pipeline處於「中止」狀態時,纔會執行,通常是由於Pipeline被手動中止。通常在具有灰色指示的Web UI中表示。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

pipeline {

    agent any

    stages {

        stage('Example') {

            steps {

                echo 'Hello World'

            }

        }

    }

    post {

        always {

            echo 'I will always say Hello again!'

        }

    }

}

stages

  包含一個或多個stage的序列,Pipeline的大部分工作在此執行。建議stages至少包含至少一個stage指令,用於連線各個交付過程,如構建,測試和部署等。
  

steps

  steps包含一個或多個在stage塊中執行的step序列。
  

1

2

3

4

5

6

7

8

9

10

pipeline {

    agent any

    stages {

        stage('Example') {

            steps {

                echo 'Hello World'

            }

        }

    }

}

  

2.Directives (指令)

  environment

environment指令指定一系列鍵值對,這些鍵值對將被定義爲所有step或stage-specific step的環境變數,具體取決於environment指令在Pipeline中的位置。
該指令支援一種特殊的方法credentials(),可以通過其在Jenkins環境中的識別符號來存取預定義的憑據。
對於型別爲「Secret Text」的憑據,該 credentials()方法將確保指定的環境變數包含Secret Text內容;對於「標準使用者名稱和密碼」型別的憑證,指定的環境變數將被設定爲username:password。
  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

pipeline {

    agent any

    environment {

        CC = 'clang'

    }

    stages {

        stage('Example') {

            environment {

                AN_ACCESS_KEY = credentials('my-prefined-secret-text')

            }

            steps {

                sh 'printenv'

            }

        }

    }

}

  

options

options指令允許在Pipeline本身內設定Pipeline專用選項。Pipeline本身提供了許多選項,例如buildDiscarder,但它們也可能由外掛提供,例如 timestamps。

  

可用選項

  buildDiscarder
    pipeline保持構建的最大個數。例如:options { buildDiscarder(logRotator(numToKeepStr: '1')) }
  disableConcurrentBuilds
    不允許並行執行Pipeline,可用於防止同時存取共用資源等。例如:options { disableConcurrentBuilds() }
  skipDefaultCheckout
    預設跳過來自原始碼控制的程式碼。例如:options { skipDefaultCheckout() }
  skipStagesAfterUnstable
    一旦構建狀態進入了「Unstable」狀態,就跳過此stage。例如:options { skipStagesAfterUnstable() }
  timeout
    設定Pipeline執行的超時時間。例如:options { timeout(time: 1, unit: 'HOURS') }
  retry
    失敗後,重試整個Pipeline的次數。例如:options { retry(3) }
  timestamps
    預定義由Pipeline生成的所有控制檯輸出時間。例如:options { timestamps() }

1

2

3

4

5

6

7

8

9

10

11

12

13

pipeline {

    agent any

    options {

        timeout(time: 1, unit: 'HOURS')

    }

    stages {

        stage('Example') {

            steps {

                echo 'Hello World'

            }

        }

    }

} 

parameters

parameters指令提供使用者在觸發Pipeline時的參數列表。這些參數值通過該params物件可用於Pipeline步驟,具體用法如下

  

可用參數
  string
    A parameter of a string type, for example: parameters { string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: '') }
  booleanParam
    A boolean parameter, for example: parameters { booleanParam(name: 'DEBUG_BUILD', defaultValue: true, description: '') }
  目前只支援[booleanParam, choice, credentials, file, text, password, run, string]這幾種參數型別,其他高階參數化型別還需等待社羣支援。

1

2

3

4

5

6

7

8

9

10

11

12

13

pipeline {

    agent any

    parameters {

        string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')

    }

    stages {

        stage('Example') {

            steps {

                echo "Hello ${params.PERSON}"

            }

        }

    }

}

triggers

triggers指令定義了Pipeline自動化觸發的方式。對於與原始碼整合的Pipeline,如GitHub或BitBucket,triggers可能不需要基於webhook的整合也已經存在。目前只有兩個可用的觸發器:cron和pollSCM。

  

cron
  接受一個cron風格的字串來定義Pipeline觸發的常規間隔,例如: triggers { cron('H 4/* 0 0 1-5') }
pollSCM
  接受一個cron風格的字串來定義Jenkins檢查SCM源更改的常規間隔。如果存在新的更改,則Pipeline將被重新觸發。例如:triggers { pollSCM('H 4/* 0 0 1-5') }

1

2

3

4

5

6

7

8

9

10

11

12

13

pipeline {

    agent any

    triggers {

        cron('H 4/* 0 0 1-5')

    }

    stages {

        stage('Example') {

            steps {

                echo 'Hello World'

            }

        }

    }

}

stage

 stage指令在stages部分中,應包含stop部分,可選agent部分或其他特定於stage的指令。實際上,Pipeline完成的所有實際工作都將包含在一個或多個stage指令中。

  

1

2

3

4

5

6

7

8

9

10

pipeline {

    agent any

    stages {

        stage('Example') {

            steps {

                echo 'Hello World'

            }

        }

    }

} 

tools

 通過tools可自動安裝工具,並放置環境變數到PATH。如果agent none,這將被忽略。

  

Supported Tools(Global Tool Configuration)
  maven
  jdk
  gradle

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

pipeline {

    agent any

    tools {

        //工具名稱必須在Jenkins 管理Jenkins → 全域性工具設定中預設定。

        maven 'apache-maven-3.0.1'

    }

    stages {

        stage('Example') {

            steps {

                sh 'mvn --version'

            }

        }

    }

}

  

when

  when指令允許Pipeline根據給定的條件確定是否執行該階段。該when指令必須至少包含一個條件。如果when指令包含多個條件,則所有子條件必須爲stage執行返回true。這與子條件巢狀在一個allOf條件中相同(見下面 下麪的例子)。
更復雜的條件結構可使用巢狀條件建:not,allOf或anyOf。巢狀條件可以巢狀到任意深度。

  

  內建條件

  branch
    當正在構建的分支與給出的分支模式匹配時執行,例如:when { branch 'master' }。請注意,這僅適用於多分支Pipeline。
  environment
    當指定的環境變數設定爲給定值時執行,例如: when { environment name: 'DEPLOY_TO', value: 'production' }
  expression
    當指定的Groovy表達式求值爲true時執行,例如: when { expression { return params.DEBUG_BUILD } }
  not
    當巢狀條件爲false時執行。必須包含一個條件。例如:when { not { branch 'master' } }
  allOf
    當所有巢狀條件都爲真時執行。必須至少包含一個條件。例如:when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } }
  anyOf
    當至少一個巢狀條件爲真時執行。必須至少包含一個條件。例如:when { anyOf { branch 'master'; branch 'staging' } }

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

pipeline {

    agent any

    stages {

        stage('Example Build') {

            steps {

                echo 'Hello World'

            }

        }

        stage('Example Deploy') {

            when {

                allOf {

                    branch 'production'

                    environment name: 'DEPLOY_TO', value: 'production'

                }

            }

            steps {

                echo 'Deploying'

            }

        }

    }

}

  

3.Parallel(並行)

Declarative Pipeline近期新增了對並行巢狀stage的支援,對耗時長,相互不存在依賴的stage可以使用此方式提升執行效率。除了parallel stage,單個parallel裡的多個step也可以使用並行的方式執行。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

pipeline {

    agent any

    stages {

        stage('Non-Parallel Stage') {

            steps {

                echo 'This stage will be executed first.'

            }

        }

        stage('Parallel Stage') {

            when {

                branch 'master'

            }

            parallel {

                stage('Branch A') {

                    agent {

                        label "for-branch-a"

                    }

                    steps {

                        echo "On Branch A"

                    }

                }

                stage('Branch B') {

                    agent {

                        label "for-branch-b"

                    }

                    steps {

                        echo "On Branch B"

                    }

                }

            }

        }

    }

}

  

4.Steps(步驟)

Declarative Pipeline可以使用 Pipeline Steps reference中的所有可用步驟 ,並附加以下僅在Declarative Pipeline中支援的步驟。

script

script步驟需要一個script Pipeline,並在Declarative Pipeline中執行。對於大多數用例,script在Declarative Pipeline中的步驟不是必須的,但它可以提供一個有用的加強。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

pipeline {

    agent any

    stages {

        stage('Example') {

            steps {

                echo 'Hello World'

 

                script {

                    def browsers = ['chrome''firefox']

                    for (int i = 0; i < browsers.size(); ++i) {

                        echo "Testing the ${browsers[i]} browser"

                    }

                }

            }

        }

    }

}

  

四、Scripted Pipeline

  Groovy指令碼不一定適合所有使用者,因此jenkins建立了Declarative pipeline,爲編寫Jenkins管道提供了一種更簡單、更有主見的語法。但是不可否認,由於指令碼化的pipeline是基於groovy的一種DSL語言,所以與Declarative pipeline相比爲jenkins使用者提供了更巨大的靈活性和可延伸性。

1.流程控制

  pipeline指令碼同其它指令碼語言一樣,從上至下順序執行,它的流程控製取決於Groovy表達式,如if/else條件語句,舉例如下:

1

2

3

4

5

6

7

8

9

10

Jenkinsfile (Scripted Pipeline)

node {

    stage('Example') {

        if (env.BRANCH_NAME == 'master') {

            echo 'I only execute on the master branch'

        else {

            echo 'I execute elsewhere'

        }

    }

}

  pipeline指令碼流程控制的另一種方式是Groovy的例外處理機制 機製。當任何一個步驟因各種原因而出現異常時,都必須在Groovy中使用try/catch/finally語句塊進行處理,舉例如下:

1

2

3

4

5

6

7

8

9

10

11

12

Jenkinsfile (Scripted Pipeline)

node {

    stage('Example') {

        try {

            sh 'exit 1'

        }

        catch (exc) {

            echo 'Something failed, I should sound the klaxons!'

            throw

        }

    }

}

  

2.Steps

  pipeline最核心和基本的部分就是「step」,從根本上來說,steps作爲Declarative pipeline和Scripted pipeline語法的最基本的語句構建塊來告訴jenkins應該執行什麼操作。
Scripted pipeline沒有專門將steps作爲它的語法的一部分來介紹,但是在Pipeline Steps reference這篇文件中對pipeline及其外掛涉及的steps做了很詳細的介紹。如有需要可參考jenkins官網對該部分的介紹Pipeline Steps reference

3.Differences from plain Groovy

  由於pipeline的一些個性化需求,比如在重新啓動jenkins後要求pipeline指令碼仍然可以執行,那麼pipeline指令碼必須將相關數據做序列化,然而這一點 Groovy並不能完美的支援,例如collection.each { item -> /* perform operation */ }

4.Declarative pipeline和Scripted pipeline的比較

共同點:
  兩者都是pipeline程式碼的持久實現,都能夠使用pipeline內建的外掛或者外掛提供的steps,兩者都可以利用共用庫擴充套件。
區別:
  兩者不同之處在於語法和靈活性。Declarative pipeline對使用者來說,語法更嚴格,有固定的組織結構,更容易生成程式碼段,使其成爲使用者更理想的選擇。但是Scripted pipeline更加靈活,因爲Groovy本身只能對結構和語法進行限制,對於更復雜的pipeline來說,使用者可以根據自己的業務進行靈活的實現和擴充套件。