简体   繁体   中英

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. 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. It appears that the GroovyClassLoader is throwing this error.

I tried to remove the loadedScripts via currentBuild.rawBuild.getExecution().loadedScripts but that didn't help. 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. 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. Then another pipeline that does the same for "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. 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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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