简体   繁体   English

Jenkins - 在单个作业中多次加载 groovy 脚本,但具有不同的阶段

[英]Jenkins - load groovy script multiple times in a single job but with different stages

I'm having a Jenkins pipeline that does both build and deploy tasks in a scripted pipeline.我有一个 Jenkins 管道,它可以在脚本管道中构建和部署任务。 The pipeline is maintained in a central repository.管道维护在中央存储库中。 For each deployment stage, it will wait for the user input and the wait time can be up to several days.对于每个部署阶段,它都会等待用户输入,等待时间可能长达数天。 During the waiting period, if the pipeline script got changed, I want the latest script to be loaded after the deployment approval (user input).在等待期间,如果管道脚本发生更改,我希望在部署批准(用户输入)后加载最新的脚本。

The first load step of the script is working fine and when I try to load again, I'm getting an error below.脚本的第一个load步骤工作正常,当我再次尝试加载时,出现以下错误。 It appears that the GroovyClassLoader is throwing this error. GroovyClassLoader 似乎抛出了这个错误。

I tried to remove the loadedScripts via currentBuild.rawBuild.getExecution().loadedScripts but that didn't help.我试图通过currentBuild.rawBuild.getExecution().loadedScripts loadedScripts的脚本,但这没有帮助。 Is there a way to achieve my use-case of reloading the script on every stage?有没有办法实现我在每个阶段重新加载脚本的用例? Any pointers would be helpful.任何指针都会有所帮助。

java.lang.LinkageError: loader org.jenkinsci.plugins.workflow.cps.CpsGroovyShell$CleanGroovyClassLoader @8d141543 attempted duplicate class definition for Script1. (Script1 is in unnamed module of loader org.jenkinsci.plugins.workflow.cps.CpsGroovyShell$CleanGroovyClassLoader

// pipeline.groovy
def config 
node('agent') {
    stage('build') {
        config = loadScripts()
        echo 'perform some tasks'
    }
    stage('deploy-dev') {
        input message: 'approve'
        config = loadScripts()
        echo 'perform some tasks'
    }
    stage('deploy-sit') {
        input message: 'approve'
        config = loadScripts()
        echo 'perform some tasks'
    }
}

def loadScripts() {
    checkout scm
    config = load 'somepath/external.groovy'
    return config
}

// somepath/external.groovy

class JobConfig {
    def someDataVar1
    def someDataVar2
}

def sayHello() {
    println 'hello'
}

return this

在此处输入图像描述

也许我不完全理解你的问题(在这种情况下,我道歉),但我不明白为什么你的 'somepath/external.groovy' 不是一个简单的 groovy 库,(像这样: http://rtyler .github.io/jenkins.io/doc/book/pipeline/shared-libraries/ ),在管道开始时声明为已使用,然后您的 JobConfig 和 sayHello 可以根据需要多次调用并具有不同的行为基于一些参数 - 比如舞台名称。

The short answer is that shared libraries are intended to solve this sort of issue.简短的回答是共享库旨在解决此类问题。 If that's not an option, then some restructuring of your script file may resolve this as a workaround.如果这不是一个选项,那么您的脚本文件的一些重组可能会解决这个问题作为一种解决方法。

The error you're receiving is stating that you're redefining a class with the same name which is not allowed.您收到的错误表明您正在重新定义一个不允许的同名类。 Since you're loading the same script a second time, that's basically the same as loading a class of the same name twice.由于您第二次加载相同的脚本,这与两次加载同名的类基本相同。

Option 1 would be to split your pipeline into multiple pipelines that all ingest the same config.选项 1 是将您的管道拆分为多个管道,这些管道都摄取相同的配置。 I've personally never had good luck with long-running pipelines that wait for user input to continue.对于等待用户输入继续的长时间运行的管道,我个人从来没有好运过。 Instead, you could split your pipeline into a "build" pipeline that basically loads your config and runs the build step, then later on when you're ready to deploy, you run a "deploy-dev" pipeline that loads the config, then runs a deploy-dev step.相反,您可以将您的管道拆分为一个“构建”管道,该管道基本上加载您的配置并运行构建步骤,然后当您准备好部署时,您运行一个加载配置的“deploy-dev”管道,然后运行部署开发步骤。 Then another pipeline that does the same for "deploy-sit".然后是另一个对“deploy-sit”执行相同操作的管道。

// pipeline-build.groovy
def config 
node('agent') {
    stage('build') {
        config = loadScripts()
        echo 'perform some tasks'
    }
}

def loadScripts() {
    checkout scm
    config = load 'somepath/external.groovy'
    return config
}

// pipeline-deploy-dev.groovy
def config 
node('agent') {
    stage('deploy-dev') {
        input message: 'approve'
        config = loadScripts()
        echo 'perform some tasks'
    }
}

def loadScripts() {
    checkout scm
    config = load 'somepath/external.groovy'
    return config
}

// pipeline-deploy-sit.groovy
def config 
node('agent') {
    stage('deploy-sit') {
        input message: 'approve'
        config = loadScripts()
        echo 'perform some tasks'
    }
}

def loadScripts() {
    checkout scm
    config = load 'somepath/external.groovy'
    return config
}

Option 2 would be to split the script into multiple scripts.选项 2 是将脚本拆分为多个脚本。 One script would contain the class definition and would only be loaded a single time at the top of the pipeline.一个脚本将包含类定义,并且只会在管道顶部加载一次。 Then other scripts would contain each of your other methods which will be used by each pipeline step.然后其他脚本将包含每个管道步骤将使用的其他每个方法。 This would look something like this:这看起来像这样:

// pipeline.groovy
def config 
node('agent') {
    loadScript("somepath/JobConfig.groovy")
    stage('build') {
        config = loadScript("somepath/Build.groovy")
        echo 'perform some tasks'
    }
    stage('deploy-dev') {
        input message: 'approve'
        config = loadScript("somepath/DeployDev.groovy")
        echo 'perform some tasks'
    }
    stage('deploy-sit') {
        input message: 'approve'
        config = loadScript("somepath/DeploySit.groovy")
        echo 'perform some tasks'
    }
}

def loadScript(def script) {
    checkout scm
    config = load script
    return config
}

Then when you load the later scripts, they can be brought up to date and loaded without redefining the initial class definition or redefining individual methods (which I imagine would also be an issue).然后,当您加载后面的脚本时,它们可以更新并加载,而无需重新定义初始类定义或重新定义单个方法(我想这也是一个问题)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM