jenkins2 pipeline裏groovy的高級用法。翻譯自:https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.mdjava
文章來自:http://www.ciandcd.com
文中的代碼來自能夠從github下載: https://github.com/ciandcdnode
1. 在groovy裏使用函數,條件控制,循環,異常捕獲等git
node('remote') {
git url: 'https://github.com/jglick/simple-maven-project-with-tests.git'
def v = version()
if (v) {
echo "Building version ${v}"
}
def mvnHome = tool 'M3'
sh "${mvnHome}/bin/mvn -B -Dmaven.test.failure.ignore verify"
step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true])
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
}
def version() {
def matcher = readFile('pom.xml') =~ '<version>(.+)</version>'
matcher ? matcher[0][1] : null
}github
上面的代碼中:正則表達式
def用來定義groovy變量或函數;安全
=~表示正則表達式的匹配;網絡
master[0][1]表示取出匹配結果中第一個匹配項中的第一個group的值;多線程
readFile爲groovy的函數用來從workspace裏讀取文件返回文件的內容,同時還可使用writeFile來保存內容到文件,fileExists用來判斷文件是否存在;app
注意 groovy腳本框下面的checkbox:use groovy sandbox,若是選中的話,groovy 腳本會在沙盒裏運行有限的功能, 不然若是不是管理員運行的話會報錯或者仍然運行就的腳本,須要管理員來approve groovy script。maven
若是遇到RejectedAccessException權限問題,須要jenkins管理員在Manage Jenkins » In-process Script Approval中approve 權限staticMethod org.codehaus.groovy.runtime.ScriptBytecodeAdapter findRegex java.lang.Object java.lang.Object。
2. 本地變量的序列化
以下的代碼在運行時可能會遇到錯誤java.io.NotSerializableException: java.util.regex.Matcher,錯誤的緣由是Matcher是不可序列化的類型。
node('remote') {
git url: 'https://github.com/jglick/simple-maven-project-with-tests.git'
def matcher = readFile('pom.xml') =~ '<version>(.+)</version>'
if (matcher) {
echo "Building version ${matcher[0][1]}"
}
def mvnHome = tool 'M3'
sh "${mvnHome}/bin/mvn -B -Dmaven.test.failure.ignore verify"
step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true])
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
}
pipeline job爲了支持可以在jenkins重啓後恢復繼續運行,jenkins在後臺按期地將job的運行狀態保存到硬盤。保存的動做通常在每一個step結束後,或者在一些step的中間,例如sh step的中間。
jenkins保存的job的狀態,包括整個控制流程,例如局部變量,循環所在的位置,等等。正由於如此,groovy裏的任何變量必須是number,string或可序列化的類型,其餘的例如網絡鏈接等是不可以序列化的。
若是你臨時地使用不可序列化的類型,則須要在使用完立刻釋放。若是局部變量在函數中,函數調用結束的時候局部變量也會被自動釋放。咱們也能夠顯示地釋放局部變量。 以下
node('remote') {
git url: 'https://github.com/jglick/simple-maven-project-with-tests.git'
def matcher = readFile('pom.xml') =~ '<version>(.+)</version>'
if (matcher) {
echo "Building version ${matcher[0][1]}"
}
matcher = null
def mvnHome = tool 'M3'
sh "${mvnHome}/bin/mvn -B -Dmaven.test.failure.ignore verify"
step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true])
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
}
然而最安全的方法是將不可序列化的語句隔離到函數中,且在函數的前面增長屬性@NonCPS。經過這種方法pipeline將識別此函數爲native且不保存對應的局部變量。另外使用了@NoCPS的函數中不可以調用其餘的pipeline steps,例如必須將readFile放到函數外面:
node('remote') {
git url: 'https://github.com/jglick/simple-maven-project-with-tests.git'
def v = version(readFile('pom.xml'))
if (v) {
echo "Building version ${v}"
}
def mvnHome = tool 'M3'
sh "${mvnHome}/bin/mvn -B -Dmaven.test.failure.ignore verify"
step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true])
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
}
@NonCPS
def version(text) {
def matcher = text =~ '<version>(.+)</version>'
matcher ? matcher[0][1] : null
}
上面的加了@NoCPS的version函數將被正常的groovy運行時執行,因此任何的局部變量都是容許的。
3. 建立多線程
pipeline可以使用parallel來同時執行多個任務。 parallel的調用須要傳入map類型做爲參數,map的key爲名字,value爲要執行的groovy腳本。
爲了測試parallel的運行,能夠安裝parallel test executor插件。此插件能夠將運行緩慢的測試分割splitTests。
用下面的腳本新建pipeline job:
node('remote') {
git url: 'https://github.com/jenkinsci/parallel-test-executor-plugin-sample.git'
archive 'pom.xml, src/'
}
def splits = splitTests([$class: 'CountDrivenParallelism', size: 2])
def branches = [:]
for (int i = 0; i < splits.size(); i++) {
def exclusions = splits.get(i);
branches["split${i}"] = {
node('remote') {
sh 'rm -rf *'
unarchive mapping: ['pom.xml' : '.', 'src/' : '.']
writeFile file: 'exclusions.txt', text: exclusions.join("\n")
sh "${tool 'M3'}/bin/mvn -B -Dmaven.test.failure.ignore test"
step([$class: 'JUnitResultArchiver', testResults: 'target/surefire-reports/*.xml'])
}
}
}
parallel branches
若是遇到RejectedAccessException錯誤,須要管理員approve權限staticMethod org.codehaus.groovy.runtime.ScriptBytecodeAdapter compareLessThan java.lang.Object java.lang.Object。
當第一次運行上面的pipeline job的時候,全部的測試順序執行。當第二次或之後執行的時候,splitTests將會將全部的測試分割爲大概等價的兩份,而後兩個task並行運行。若是兩個task運行在不一樣的slave上,則能夠看到job總的時間將會減半。
下面的等價語句用來打包pom.xml和源代碼:
archive 'pom.xml, src/'
step([$class: 'ArtifactArchiver', artifacts: 'pom.xml, src/'])
咱們能夠看到prallel裏的語句使用了node,這意味着並行執行的任務將會在新的node/slave上執行,且使用不一樣的workspace,爲了確保全部的node和workspace使用相同的代碼,因此纔有了前面的打包archive和parallel裏的解包unarchive。
上面的例子中咱們能夠看到同一個pipeline job裏可使用多個node,多個node會有不一樣的workspace,咱們須要確保每一個workspace的內容都是咱們想要的內容。
另外一個問題,若是在pipeline中使用env,環境變量的修改會在整個pipeline起做用,若是隻修改parallel並行的線程的變量,可使用withEnv。
在使用了parallel的console log裏,並行的log都混在了一塊兒,須要在job的pipeline steps頁面查看按邏輯分割的更狀況的log。
4. 建立stage
默認地,pipeline的多個job能夠並行地運行。使用stage能夠限制job裏的某些階段的並行數量。新的job用於更高的優先級,舊的job遇到stage的並行限制會直接退出。
並行性的限制有的時候頗有用,例如部署到單個server,在同一時間只能有最新的一個job部署。
5. 從外部加載groovy腳本