【问题标题】:Jenkins shared libraries, multiple versions of same classJenkins共享库,同一个类的多个版本
【发布时间】:2021-08-18 17:59:48
【问题描述】:

我正在尝试加载相同 Jenkins shared library 的两个版本。这个库主要包含 Jenkins 和管道的辅助函数。

需要两个版本的原因是我们的许多管道都有一些序言/尾声,通常使用这些辅助函数的最新版本,而在这之间,会调用特定项目的管道,这可能需要旧版本辅助函数的版本。这样,更新辅助函数就不需要修改旧管道。

到目前为止,我正在尝试这样的事情:

def latestlib = library identifier: 'jenkinslib-latest@default', retriever: legacySCM(
    [$class: 'SubversionSCM', 
        locations: [[
            depthOption: 'infinity', 
            ignoreExternalsOption: false, 
            remote: "svn://../jenkinslib/branches/<latest-branch>"
        ]], 
        workspaceUpdater: [
            $class: 'CheckoutUpdater'
        ]
    ]
)

def olderlib = library identifier: 'jenkinslib-old@default', retriever: legacySCM(
    [$class: 'SubversionSCM', 
        locations: [[
            depthOption: 'infinity', 
            ignoreExternalsOption: false, 
            remote: "svn://../jenkinslib/branches/<old-branch>"
        ]], 
        workspaceUpdater: [
            $class: 'CheckoutUpdater'
        ]
    ]
)

这个库(它的两个版本)在 src/org/jenkinslib/HelperFunctions.groovy 有一个类 HelperFunctions,看起来像这样:

package org.jenkinslib

class HelperFunctions implements Serializable {
 ...
}

回到管道,我尝试创建HelperFunction 两个类的实例:

def hf = latestlib.org.jenkinslib.HelperFunctions.new()
def hf2 = olderlib.org.jenkinslib.HelperFunctions.new()

这失败了:

java.lang.LinkageError:加载程序(org/jenkinsci/plugins/workflow/cps/CpsGroovyShell$CleanGroovyClassLoader 的实例):尝试重复的类定义名称:“org/jenkinslib/HelperFunctions”

我想这是有道理的。有没有办法解决这个问题?

【问题讨论】:

    标签: jenkins groovy jenkins-groovy


    【解决方案1】:

    单独参考问题的标题,根据Jenkins doc Dependencies and Class Loading, The plugin class loader hierarchy应该可以使用插件:

    每个(启用的)插件都有自己的ClassLoader两个不同的插件可以定义一个同名的类,只要它们都不依赖于另一个。

    (我强调。)

    Groovy 显然正在搜索其完整的classpath,我看不到其他方式 atm。见Writing Pipeline-Compatible Plugins

    【讨论】:

      【解决方案2】:

      在不同程度上(几乎)起作用的几件事:

      需要注意的一点是,在每个分支的基础上修改库版本,以便 OP 中的实例化看起来更像这样:

      def hf = latestlib.org.jenkinslib.HelperFunctions.new()
      def hf2 = olderlib.org.jenkinslib2.HelperFunctions.new()
      

      (注意不同的包名)

      ... 似乎有效。我对java并没有过多的经验,据我所知,这需要修改每个分支库中的所有package语句,这不是很好。

      由于这些 groovy 文件都只是文本,因此可以执行任意数量的手动“名称修改”操作,您可以在其中即时修改包名甚至类名,然后加载共享库从磁盘而不是 SCM,如下所述:https://stackoverflow.com/a/51325467/2002288(或类似)。

      我尝试的另一种方法是涉及这样的代码:

      String sourceFile = readFile("./...branch1.../HelperFunctions.groovy");
      Class groovyClass = new GroovyClassLoader(getClass().getClassLoader()).parseClass(sourceFile);
      GroovyObject hf = (GroovyObject) groovyClass2.newInstance();
      

      ... 再次针对HelperFunctions 的另一个分支/版本。这种工作,但我相信以这种方式加载的代码不会经历 Jenkins CPS 转换,所以如果你将原生 jenkins 的东西(如sh)(这是 CPS 转换)混合到你的类中,你会得到错误像这样:

      预计会调用 org.jenkinslib.HelperFunctions.GetBuildTool 但最终捕获了 sh;见:https://jenkins.io/redirect/pipeline-cps-method-mismatches/

      @Gerold Broser 建议的插件方法可能是一个可行的建议,但我自己没有尝试过。

      我相信,有效的方法是使用本机管道load 命令,前提是您的 groovy 文件只包含免费函数。那是在实用性方面放弃了一些东西,并且真的放弃了“库”以支持一堆功能,但目前为我检查了最多的框。简而言之,此解决方案如下所示:

      def old_functions = load '.../branch1/.../HelperFunctions.groovy'
      def new_functions = load '.../branch2/.../HelperFunctions.groovy'
      
      println(old_functions.f())
      println(new_functions.f())
      

      【讨论】:

        猜你喜欢
        • 2010-09-18
        • 1970-01-01
        • 1970-01-01
        • 2019-01-22
        • 2021-08-30
        • 1970-01-01
        • 1970-01-01
        • 2015-09-03
        • 1970-01-01
        相关资源
        最近更新 更多