Разбор XML-файла в конвейере Jenkins
У меня есть файл XML, который я хотел бы использовать в качестве входных данных для сценария конвейера. Проблема в том, что XMLParser не сериализуем, поэтому я поместил его в функцию NonCPS, но из-за этого потерял объект Node.
Это конвейерный скрипт:
def buildPlanPath = 'C:\\buildPlan_test.xml'
@NonCPS
groovy.util.Node getBuildPlan(path) {
new XmlParser().parseText(readFile(path))
}
node {
//def buildPlan = new XmlParser().parseText(readFile(buildPlanPath))
groovy.util.Node buildPlan = getBuildPlan(buildPlanPath)
println buildPlan.getClass()
println buildPlan
println buildPlan.branch
}
Это входной пример:
<branch name='mybranch'>
<stage>
<job name='job11' />
<job name='job12' />
</stage>
<stage>
<job name='job21' />
<job name='job22' />
<job name='job23' />
</stage>
<stage>
<job name='job31' />
</stage>
</branch>
Это результат:
Started by user admin
[Pipeline] node
Running on master in C:\Jenkins\workspace\pipeline-develop
[Pipeline] {
[Pipeline] readFile
[Pipeline] echo
class java.lang.String
[Pipeline] echo
<branch name='mybranch'>
<stage>
<job name='job11' />
<job name='job12' />
</stage>
<stage>
<job name='job21' />
<job name='job22' />
<job name='job23' />
</stage>
<stage>
<job name='job31' />
</stage>
</branch>
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
groovy.lang.MissingPropertyException: No such property: branch for class: java.lang.String
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:53)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.getProperty(ScriptBytecodeAdapter.java:458)
at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.getProperty(DefaultInvoker.java:25)
at com.cloudbees.groovy.cps.impl.PropertyAccessBlock.rawGet(PropertyAccessBlock.java:17)
at WorkflowScript.run(WorkflowScript:16)
at ___cps.transform___(Native Method)
at com.cloudbees.groovy.cps.impl.PropertyishBlock$ContinuationImpl.get(PropertyishBlock.java:62)
at com.cloudbees.groovy.cps.LValueBlock$GetAdapter.receive(LValueBlock.java:30)
at com.cloudbees.groovy.cps.impl.PropertyishBlock$ContinuationImpl.fixName(PropertyishBlock.java:54)
at sun.reflect.GeneratedMethodAccessor327.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
at com.cloudbees.groovy.cps.impl.ConstantBlock.eval(ConstantBlock.java:21)
at com.cloudbees.groovy.cps.Next.step(Next.java:58)
at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:154)
at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:164)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:276)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$000(CpsThreadGroup.java:78)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:185)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:183)
at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:47)
at java.util.concurrent.FutureTask.run(Unknown Source)
at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:112)
at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Finished: FAILURE
Я использую Jenkins 2.7 с конвейером 2.1, который в настоящее время является последним.
5 ответов
В конце концов я думаю, что мой подход был неправильным: я решил преобразовать файл XML в отдельный скрипт Groovy и загрузить его в конвейер.
Обновление: недавно люди начали редактировать мой ответ для ясности, но факт в том, что я просто отказался от сохранения своей конфигурации в файлах XML и выбрал отличные скрипты, что дало мне больше гибкости. Я понимаю, что это не может быть обычной практикой, но это соответствует моим потребностям.
Например - вместо:
config.xml:
<settings>
<floopi>2</floopi>
</settings>
Я использовал:
config.groovy:
def call() {[
floopi: 2
]}
return this
И в сценарии конвейера:
stage('init') {
def settings = load('config.groovy')()
echo "floopi: ${settings.floopi}"
}
Я надеюсь, что это лучший ответ:)
Вы можете использовать XmlSlurper, он работает для меня.
def xmlText = new XmlSlurper().parse(MyURL)
xmlText.data.artifact.each {******
Так как ветвь является корневым элементом, вам не нужно явно указывать его при доступе к вашим проанализированным узлам
Попробуйте изменить
println buildPlan.branch
к
println buildPlan.stage
Распечатать узлы сцены
@NonCPS
метод должен только принять или вернуть Serializable
типы. Попробуй вернуться .branch
из метода.
Могу я порекомендовать общую библиотеку . Это позволит вам
- централизовать логику
- писать модульные тесты - через модуль конвейера jenkins и, возможно, Spock
- используйте такой подход
Src dir
class PipelineSupport {
PipelineSupport(def env, def jenkinsStepAccess) {
this.env = env
this.jenkinsStepAccess = jenkinsStepAccess
}
def readXml(def path) {
def text = jenkinsStepAccess.readFile(path)
def parser = new XmlParser()
def xml = parser.parseText(text.toString())
return xml
}
}
vars dir var / foo.groovy
import ...PipelineSupport
Map readXml(def path) {
// pass env and "this" access point to jenkins DSL to calls
return new PipelineSupport(env, this).readXml(path)
}
использовать в сборке
library "mylib"
def xmlData = foo.readXml('path/to/bar.xml')