在 Jenkins Pipeline 中,使用 Groovy 语言进行共享库的开发。从理论上讲,我们可以按照需求,开发我们想要的任何功能。但是,现实中总会遇到一些棘手的问题。比如这次遇到的 Dependency hell - 我们在共享库中,通过 Grape 引入我们需要的模块,这些模块又依赖于其他模块,然而这些模块与 Jenkins 正在使用的模块冲突。
比如,我们需要使用 [[https://github.com/ThStock/docker-java-parser|ThStock/docker-java-parser]] 模块解析 Dockerfile 文件,以检查其是否符合我们的规范。但是 [[https://github.com/ThStock/docker-java-parser|ThStock/docker-java-parser]] 模块需要 commons-lang 3.5 模块,而 Jenkins 使用 commons-lang 2.6 版本。这便是冲突。
该笔记将记录:如何解决 Jenkins Pipeline 共享库的依赖问题,以及相关问题处理。
在多数 常规的 自动化场景中,不会遇到该问题,只是我们的需求太特殊了,需要解决这个问题。
既然存在冲突,导致部分任务我们无法通过 Pipeline 共享库实现,那么我们可以编写自定义脚本来实现(比如通过 python 脚本实现某个极度复杂的统计功能),然后在 Pipeline 中执行该脚本。
现在,我们还需要将该脚本保存到 Pipeline 共享库中,作为共享库的组件,与其他用户分享,毕竟这个脚本也是共享库的一部分。根据目前(02/20/2021)的共享库的要求,只能保存在 resources/ 目录中。现在的问题就是:我们如何执行 resources/ 目录中的脚本
在 Jenkins Pipeline 中,可以在共享库的 resources/ 目录中保存资源文件,然后通过 def text = libraryResource ‘data.yaml‘ 的方式读取资源文件。
整个过程就是这样:1)通过 libraryResource 读取脚本内容,2)然后使用 writeFile 写入外部文件,2)再执行该外部文件
程序示例:
def functionsContent = libraryResource ‘com/mycorp/pipeline/somelib/functions.sh‘ writeFile file: ‘/tmp/functions.sh‘, text: functionsContent sh "sh /tmp/function.sh"
方案一有个问题:如果我们有很多脚本,那么这些脚本只能独立执行,不能相互引用。这是因为脚本是被先读取出来,然后再执行,我们不知道正在读取的脚本中引用的文件,因此就无法同时读取相关的文件。这就导致不同脚本中,会出现大量重复代码。(也不是没有可能,我们可以自己实现解析器,以注释的形式定义脚本依赖关系,然后在加载时进行解析,以加载相关依赖)
但是,办法总是有的,我们通过获取当前源码文件的路径,然后计算出脚本文件路径。这成为解决这个问题的关键。
下面的三种方法都可以获取源码文件的路径:
// 方法一 def filePath = this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath() println filePath // 方法二 import groovy.transform.SourceURI import java.nio.file.Path import java.nio.file.Paths @SourceURI URI sourceUri Path scriptLocation = Paths.get(sourceUri) println scriptLocation.toString() // 方法三 def filePath = this.class.classLoader.getResourceLoader().loadGroovySource(this.class.name).toURI() println filePath // 这些方法都将输出如下内容: /var/jenkins_home/jobs/pipeline-example/builds/22/libs/TOOLBOX/vars/fileLocation.groovy
因此我们使用如下方法,适用于全局变量(vars/)与类库(src/):
import java.nio.file.Path; import java.nio.file.Paths; def sourceFile = "/var/jenkins_home/jobs/pipeline-example/builds/22/libs/TOOLBOX/vars/fileLocation.groovy" Path path = Paths.get(sourceFile) while (dirname = path.getFileName().toString()) { if ("vars".equals(dirname) || "src".equals(dirname)) break path = path.getParent() } def resourcesFolder = path.getParent().resolve(‘resources‘).toString() println resourcesFolder // /var/jenkins_home/jobs/pipeline-example/builds/22/libs/TOOLBOX/resources
但是,该方法也有局限性:当 Jenkins 存在多个共享库,那么通过该方法定位的 resources 目录可能不是我们期望的目录。zheasdfasdf
自定义类加载器,来加载 Jar 包,以使用不同的类:
ClassLoader cl = new URLClassLoader(new URL[] {new File("v1.jar").toURL()}, Thread.currentThread().getContextClassLoader()); Class<?> clazz1 = cl.loadClass("com.abc.Hello") ClassLoader c2 = new URLClassLoader(new URL[] {new File("v2.jar").toURL()}, Thread.currentThread().getContextClassLoader()); Class<?> clazz2 = cl.loadClass("com.abc.Hello");
# 02/20/2021 目前,该方案并不适用于此问题,因为 Jar 包的管理与定位又成为另外一个问题。我们在此记录此方法,为后面提供一种思路,也许那一天就用到了。
How to invoke bash functions defined in a resource file from a Jenkins pipeline Groovy script?
Java - how to load different versions of the same class? - Stack Overflow
android - Java: How to import older version of class - Stack Overflow
How do you get the path of the running script in groovy? - Stack Overflow
groovyshell - How to use \@SourceURI annotation to retrieve the full path of the script file in Groovy 2.3?
jenkins - How to load files from resources folder in Shared library without knowing their names (or number)?
「Jenkins Pipeline」- 执行 resource 文件 @20210221
原文:https://www.cnblogs.com/k4nz/p/14425687.html