Jenkins 管道作业-dsl
Jenkins pipeline job-dsl
我正在尝试从管道步骤中 运行 一个 job-dsl 脚本。一般来说,这应该是可能的,如 here 所述,在管道步骤中添加了以下代码片段:
stage('Add new jobs') {
steps {
echo 'Scanning...'
jobDsl(additionalClasspath: 'src/breuer/jenkins/utils', removedJobAction: 'DELETE', removedViewAction: 'DELETE',
targets: 'src/breuer/jenkins/utils/DotNetJob.groovy', unstableOnDeprecation: true)
}
}
当运行连接这个管道时,Jenkins 会抱怨
ERROR: no Job DSL script(s) found at src/breuer/jenkins/utils/DotNetJob.groovy
Finished: FAILURE
为了测试目的,DotNetJob.groovy 的内容如下所示:
#!/usr/bin/env groovy
package breuer.jenkins.utils
import javaposse.jobdsl.dsl.Job
def solutions = findFiles glob: '**/*.sln'
echo "Solution count: ${solutions.size()}"
job("TestDotNet") {
steps {
shell 'echo Hello from new DotNet job'
}
}
我认为,问题在于管道作业和包含作业 dsl 的脚本位于不同的工作空间中。设置如下:
- 1 GitHub 组织
- 该组织内的 2 个存储库
- 1 Repo 包含 groovy 代码中的共享库/作业生成器
- 1 Repo 包含几个 .Net 解决方案,并且在根目录中有 Jenkinsfile
共享库回购已作为全局管道库添加到管理詹金斯->配置系统中,并为每个管道隐式加载(例如 Jenkinsfile)
现在实际代码库中的管道非常小。它只是转发到共享库中的管道定义:
#!/usr/bin/env groovy
dotNetStandardPipeline {
message = "Hello World!"
}
这很有魅力,因为全局管道库是隐式导入的。此 dotNetStandardPipeline 现在包含上述步骤,尝试调用 jobDsl 管道步骤并将目标设置为 DotNetJob.groovy 脚本,位于与 dotNetStandardPipeline 本身相同的 repo 中。
现在的问题似乎是,管道是在 'Code-Repository' 的工作区中执行的,因此路径 'src/breuer/jenkins/utils' 不存在。
我如何才能知道脚本的真实位置以及如何将 jobDsl 指定为目标,它本身位于不同的存储库中?还是我走错了路?
编辑
经过进一步调查,共享库存储库被检出到 "real" 工作区旁边的目录,似乎是事实 @libs后缀。所以我认为使用以下方法是个好主意:
script {
def wsName = "${WORKSPACE}".split("\\")[ -1 ]
echo "wsName: ${wsName}"
echo "RelDir: ../${wsName}@libs/breuer-jenkins-lib/src/breuer/jenkins/utils/DotNetJob.groovy"
jobDsl(removedJobAction: 'DELETE', removedViewAction: 'DELETE',
targets: "../${wsName}@libs/breuer-jenkins-lib/src/breuer/jenkins/utils/DotNetJob.groovy", unstableOnDeprecation: true)
}
不幸的是,这似乎完全破坏了某些东西,因为现在 jenkins 会在构建的输出中抱怨以下消息:
java.nio.file.AccessDeniedException: D:\Road to Git\Jenkins\JenkinsGit\workspace\t_TestCIIntegration_develop-RKLAJXSET2S232SE6RNISESVW75KUNU4E3CPSAAP42MHZAGO6Z2A\.git
at sun.nio.fs.WindowsException.translateToIOException(Unknown Source)
at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(Unknown Source)
at java.nio.file.Files.newByteChannel(Unknown Source)
at java.nio.file.Files.newByteChannel(Unknown Source)
at java.nio.file.spi.FileSystemProvider.newInputStream(Unknown Source)
at java.nio.file.Files.newInputStream(Unknown Source)
at hudson.FilePath.read(FilePath.java:1771)
at hudson.FilePath$read.call(Unknown Source)
at javaposse.jobdsl.plugin.ScriptRequestGenerator.readFile(ScriptRequestGenerator.groovy:103)
这么看来,即使我能够确定 groovy 文件的位置,仍然无法调用它?!
注意:当直接将src目录复制到工作空间中,然后将target参数设置为src/breuer/jenkins...等等,就可以了
这是否意味着 groovy 脚本 必须 与 jenkinsfile 在同一个 repo 中??
编辑 2
由于很难用文字解释我的计划背后的结构和想法,我在 GitHub 上创建了一个带有两个演示存储库的小型演示组织。 here 您可以找到包含两个 C# 解决方案和 jenkinsfile 的源代码存储库。自述文件描述了 CI 集成的计划。
包含 groovy 脚本的 CI 库位于 here
编辑 3 和结论
对于大多数来到这里的人,请查看mkobit给出的已接受答案(感谢您的努力!)。特别是解决实际问题的方法真的很有帮助。将 job-dsl 脚本放入资源绝对是一种选择。
同时我采用了另一种方法,我想告知一下这个方法。
我已经在 jenkins 上使用了 "GitHub Organization" 作业。目的是让它成为 唯一 手动创建的作业,并通过代码(即通过 Jenkinsfile)创建所有其他必需的作业。
我必须关心的一个真正的存储库是一个大量增长的存储库,从 svn 移动到 git,其中包含大约 300 个 .Net 解决方案。这些解决方案中的每一个都应由 jenkins 上的单独作业构建。我们 可以 在管道本身内执行此操作,但这将意味着以太在管道中有很多阶段,或者乍一看没有关于个别失败解决方案的信息。
因此,我必须为每个解决方案动态构建单独的作业。
代码库本身不应该被很多与 jenkins 相关的东西污染,所以我想严格区分这两件事。
现在,我决定不再让管道调用 job-dsl 脚本,而是在 jenkins 中手动创建一个 Freestyle-Job。这用作 Seed-Job 并具有一些参数(工作区、项目、分支等)。
现在管道将触发种子作业的构建,然后 运行 使用所需信息发送作业 dsl。
管道中的这个阶段完成后,管道将随后触发所需作业的构建。
这可能不是最优雅的解决方案,但通过这种方法,我实现了一个完全自动化的、在代码中定义的 jenkins 环境,只有两个手动创建的作业。
GitHub Branch Source 插件完成了一些事情:
- 扫描一个或多个 GitHub 个组织
- 为每个存储库生成一个文件夹作业
- 每个文件夹都会扫描具有
Jenkinsfile
(默认配置) 的重要内容(拉取请求、分支等)
- 每个值得注意的事物都有一个为其生成的管道作业
- 每个管道作业可以自动通知 GitHub 构建状态(比如在拉取请求中)
- 我确定我还缺少其他值得注意的功能
它可以通过轮询或监听事件来操作,例如拉取请求创建、拉取请求更新、分支和其他 SCM 事件。
我认为让每个存储库使用 jobDsl
步骤生成作业的 Jenkinsfile
的想法可能过于复杂(当然取决于您期望的最终目标)。 Jenkins Pipelines 的好处之一是能够将构建定义指定为代码。在此示例中,您将定义其他作业来构建项目。为什么不让 Jenkinsfile 自己构建存储库,在全局库的帮助下定义公共路径?您已经有了 GitHub Branch Source 插件提供的作业生成和扫描,所以让 Jenkinsfile
完成构建过程的艰苦工作。
好的,如果这不能令人信服或者我不完全理解您的用例,让我们尝试解决您 运行 遇到的问题。
您在方法中必须考虑一些不同的注意事项和限制
jobDsl
步骤可以通过几种不同的方式提供作业脚本:
jobDsl(targets: 'ant/pattern/for/job/files/*.groovy')
- 文件从工作区提供,目标可以是 Ant pattern
jobDsl(scriptText: "folder('myFolder')")
- 直接提供脚本文本
附加类路径 选项要求文件也位于工作区中。它还有一个额外的要求,即文件是在 JVM 类路径上工作的 classfiles/JARs/things。这意味着您必须先 assemble 工件,然后才能将它们与作业 DSL 一起使用,这对于进入消耗您的库的作业来说会很痛苦。
共享库从源代码加载,然后 Jenkins Pipelines 使用它的特殊编译器准备执行。
我可以从这些选项中想到几个不同的选项和细节:
- 不要在你的全局库中使用
additionalClasspath
- 因为它需要构建 类 (本质上)你将不得不编译你的助手 类
- 使用
jobDsl(scriptText: '<JOB_DSL_TEXT_DIRECTLY>')
- 您失去了一些仅测试作业 DSL 代码的能力,但您仍然可以进行集成样式测试以查看创建了哪些作业。 (插件传入)我建了一个Gradle plugin that allows you to test shared libraries,这样可能对测试方面有帮助。
- 如果你想将你的 Job DSL Groovy 脚本分开,你可能必须将它放在共享库的
resources
目录中,并且仍然使用 scriptText
选项。例如,如果脚本位于 resources/breuer/jenkins/utils/DotNetJob.groovy
并且您的共享库已加载,您可以执行类似 jobDsl(scriptText: libraryResource('resources/breuer/jenkins/utils/DotNetJob.groovy'))
的操作
我正在尝试从管道步骤中 运行 一个 job-dsl 脚本。一般来说,这应该是可能的,如 here 所述,在管道步骤中添加了以下代码片段:
stage('Add new jobs') {
steps {
echo 'Scanning...'
jobDsl(additionalClasspath: 'src/breuer/jenkins/utils', removedJobAction: 'DELETE', removedViewAction: 'DELETE',
targets: 'src/breuer/jenkins/utils/DotNetJob.groovy', unstableOnDeprecation: true)
}
}
当运行连接这个管道时,Jenkins 会抱怨
ERROR: no Job DSL script(s) found at src/breuer/jenkins/utils/DotNetJob.groovy
Finished: FAILURE
为了测试目的,DotNetJob.groovy 的内容如下所示:
#!/usr/bin/env groovy
package breuer.jenkins.utils
import javaposse.jobdsl.dsl.Job
def solutions = findFiles glob: '**/*.sln'
echo "Solution count: ${solutions.size()}"
job("TestDotNet") {
steps {
shell 'echo Hello from new DotNet job'
}
}
我认为,问题在于管道作业和包含作业 dsl 的脚本位于不同的工作空间中。设置如下:
- 1 GitHub 组织
- 该组织内的 2 个存储库
- 1 Repo 包含 groovy 代码中的共享库/作业生成器
- 1 Repo 包含几个 .Net 解决方案,并且在根目录中有 Jenkinsfile
共享库回购已作为全局管道库添加到管理詹金斯->配置系统中,并为每个管道隐式加载(例如 Jenkinsfile)
现在实际代码库中的管道非常小。它只是转发到共享库中的管道定义:
#!/usr/bin/env groovy
dotNetStandardPipeline {
message = "Hello World!"
}
这很有魅力,因为全局管道库是隐式导入的。此 dotNetStandardPipeline 现在包含上述步骤,尝试调用 jobDsl 管道步骤并将目标设置为 DotNetJob.groovy 脚本,位于与 dotNetStandardPipeline 本身相同的 repo 中。
现在的问题似乎是,管道是在 'Code-Repository' 的工作区中执行的,因此路径 'src/breuer/jenkins/utils' 不存在。
我如何才能知道脚本的真实位置以及如何将 jobDsl 指定为目标,它本身位于不同的存储库中?还是我走错了路?
编辑
经过进一步调查,共享库存储库被检出到 "real" 工作区旁边的目录,似乎是事实 @libs后缀。所以我认为使用以下方法是个好主意:
script {
def wsName = "${WORKSPACE}".split("\\")[ -1 ]
echo "wsName: ${wsName}"
echo "RelDir: ../${wsName}@libs/breuer-jenkins-lib/src/breuer/jenkins/utils/DotNetJob.groovy"
jobDsl(removedJobAction: 'DELETE', removedViewAction: 'DELETE',
targets: "../${wsName}@libs/breuer-jenkins-lib/src/breuer/jenkins/utils/DotNetJob.groovy", unstableOnDeprecation: true)
}
不幸的是,这似乎完全破坏了某些东西,因为现在 jenkins 会在构建的输出中抱怨以下消息:
java.nio.file.AccessDeniedException: D:\Road to Git\Jenkins\JenkinsGit\workspace\t_TestCIIntegration_develop-RKLAJXSET2S232SE6RNISESVW75KUNU4E3CPSAAP42MHZAGO6Z2A\.git
at sun.nio.fs.WindowsException.translateToIOException(Unknown Source)
at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(Unknown Source)
at java.nio.file.Files.newByteChannel(Unknown Source)
at java.nio.file.Files.newByteChannel(Unknown Source)
at java.nio.file.spi.FileSystemProvider.newInputStream(Unknown Source)
at java.nio.file.Files.newInputStream(Unknown Source)
at hudson.FilePath.read(FilePath.java:1771)
at hudson.FilePath$read.call(Unknown Source)
at javaposse.jobdsl.plugin.ScriptRequestGenerator.readFile(ScriptRequestGenerator.groovy:103)
这么看来,即使我能够确定 groovy 文件的位置,仍然无法调用它?!
注意:当直接将src目录复制到工作空间中,然后将target参数设置为src/breuer/jenkins...等等,就可以了
这是否意味着 groovy 脚本 必须 与 jenkinsfile 在同一个 repo 中??
编辑 2
由于很难用文字解释我的计划背后的结构和想法,我在 GitHub 上创建了一个带有两个演示存储库的小型演示组织。 here 您可以找到包含两个 C# 解决方案和 jenkinsfile 的源代码存储库。自述文件描述了 CI 集成的计划。
包含 groovy 脚本的 CI 库位于 here
编辑 3 和结论
对于大多数来到这里的人,请查看mkobit给出的已接受答案(感谢您的努力!)。特别是解决实际问题的方法真的很有帮助。将 job-dsl 脚本放入资源绝对是一种选择。
同时我采用了另一种方法,我想告知一下这个方法。
我已经在 jenkins 上使用了 "GitHub Organization" 作业。目的是让它成为 唯一 手动创建的作业,并通过代码(即通过 Jenkinsfile)创建所有其他必需的作业。
我必须关心的一个真正的存储库是一个大量增长的存储库,从 svn 移动到 git,其中包含大约 300 个 .Net 解决方案。这些解决方案中的每一个都应由 jenkins 上的单独作业构建。我们 可以 在管道本身内执行此操作,但这将意味着以太在管道中有很多阶段,或者乍一看没有关于个别失败解决方案的信息。
因此,我必须为每个解决方案动态构建单独的作业。
代码库本身不应该被很多与 jenkins 相关的东西污染,所以我想严格区分这两件事。
现在,我决定不再让管道调用 job-dsl 脚本,而是在 jenkins 中手动创建一个 Freestyle-Job。这用作 Seed-Job 并具有一些参数(工作区、项目、分支等)。
现在管道将触发种子作业的构建,然后 运行 使用所需信息发送作业 dsl。
管道中的这个阶段完成后,管道将随后触发所需作业的构建。
这可能不是最优雅的解决方案,但通过这种方法,我实现了一个完全自动化的、在代码中定义的 jenkins 环境,只有两个手动创建的作业。
GitHub Branch Source 插件完成了一些事情:
- 扫描一个或多个 GitHub 个组织
- 为每个存储库生成一个文件夹作业
- 每个文件夹都会扫描具有
Jenkinsfile
(默认配置) 的重要内容(拉取请求、分支等)
- 每个值得注意的事物都有一个为其生成的管道作业
- 每个管道作业可以自动通知 GitHub 构建状态(比如在拉取请求中)
- 我确定我还缺少其他值得注意的功能
它可以通过轮询或监听事件来操作,例如拉取请求创建、拉取请求更新、分支和其他 SCM 事件。
我认为让每个存储库使用 jobDsl
步骤生成作业的 Jenkinsfile
的想法可能过于复杂(当然取决于您期望的最终目标)。 Jenkins Pipelines 的好处之一是能够将构建定义指定为代码。在此示例中,您将定义其他作业来构建项目。为什么不让 Jenkinsfile 自己构建存储库,在全局库的帮助下定义公共路径?您已经有了 GitHub Branch Source 插件提供的作业生成和扫描,所以让 Jenkinsfile
完成构建过程的艰苦工作。
好的,如果这不能令人信服或者我不完全理解您的用例,让我们尝试解决您 运行 遇到的问题。
您在方法中必须考虑一些不同的注意事项和限制
jobDsl
步骤可以通过几种不同的方式提供作业脚本:
jobDsl(targets: 'ant/pattern/for/job/files/*.groovy')
- 文件从工作区提供,目标可以是 Ant patternjobDsl(scriptText: "folder('myFolder')")
- 直接提供脚本文本
附加类路径 选项要求文件也位于工作区中。它还有一个额外的要求,即文件是在 JVM 类路径上工作的 classfiles/JARs/things。这意味着您必须先 assemble 工件,然后才能将它们与作业 DSL 一起使用,这对于进入消耗您的库的作业来说会很痛苦。
共享库从源代码加载,然后 Jenkins Pipelines 使用它的特殊编译器准备执行。
我可以从这些选项中想到几个不同的选项和细节:
- 不要在你的全局库中使用
additionalClasspath
- 因为它需要构建 类 (本质上)你将不得不编译你的助手 类 - 使用
jobDsl(scriptText: '<JOB_DSL_TEXT_DIRECTLY>')
- 您失去了一些仅测试作业 DSL 代码的能力,但您仍然可以进行集成样式测试以查看创建了哪些作业。 (插件传入)我建了一个Gradle plugin that allows you to test shared libraries,这样可能对测试方面有帮助。 - 如果你想将你的 Job DSL Groovy 脚本分开,你可能必须将它放在共享库的
resources
目录中,并且仍然使用scriptText
选项。例如,如果脚本位于resources/breuer/jenkins/utils/DotNetJob.groovy
并且您的共享库已加载,您可以执行类似jobDsl(scriptText: libraryResource('resources/breuer/jenkins/utils/DotNetJob.groovy'))
的操作