You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jenkins任务持续触发MissingPropertyException问题求助

Jenkins脚本持续报MissingPropertyException问题排查与解决

问题描述

在Jenkins任务中执行Groovy脚本时,反复遇到MissingPropertyException,即使已定义相关变量。

初始报错信息

ERROR: Build step failed with exception
groovy.lang.MissingPropertyException: No such property: manager for class: Script1
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:66)
    at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:51)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:310)
    at Script1.isAnyJobRunningOrQueued(Script1.groovy:90)
    at Script1$isAnyJobRunningOrQueued.callCurrent(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:157)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:169)
    at Script1.run(Script1.groovy:48)
    at groovy.lang.GroovyShell.evaluate(GroovyShell.java:574)
    at groovy.lang.GroovyShell.evaluate(GroovyShell.java:612)
    at groovy.lang.GroovyShell.evaluate(GroovyShell.java:583)
    at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript.evaluate(SecureGroovyScript.java:442)
    at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript.evaluate(SecureGroovyScript.java:379)
    at hudson.plugins.groovy.SystemGroovy.run(SystemGroovy.java:95)
    at hudson.plugins.groovy.SystemGroovy.perform(SystemGroovy.java:59)
    at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20)
    at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:816)
    at hudson.model.Build$BuildExecution.build(Build.java:199)
    at hudson.model.Build$BuildExecution.doRun(Build.java:164)
    at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:524)
    at hudson.model.Run.execute(Run.java:1899)
    at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:44)
    at hudson.model.ResourceController.execute(ResourceController.java:107)
    at hudson.model.Executor.run(Executor.java:449)

脚本用途及原代码

脚本用于当除clean_test清理任务外,无其他Jenkins任务运行或排队时执行清理操作,原代码如下:

import hudson.model.*

def runningBuilds = []
def queuedBuilds = []

def manager = Hudson.instance

def queue = manager.queue

if (queue.items.size() > 0) {
    queuedBuilds = queue.items.collect { it.task.url }
}

for (computer in manager.computers) {
    for (executor in computer.executors) {
        if (executor.isBusy()) {
            def currentBuildUrl = executor.currentExecutable.url
            if (!currentBuildUrl.matches('.*/clean_test/\\d+/'))
                runningBuilds << currentBuildUrl
                println "excluding current job from running builds: $currentBuildUrl"
            }
        }
    }
}

def maxRetries = 70
def retryIntervalSec = 10

def retries = 0
while (retries < maxRetries) {
    if (runningBuilds.isEmpty() && queuedBuilds.isEmpty()) {
        break
    }

    if (retries == maxRetries - 1) {
        println "Max retries reached. Exiting..."
        return
    }

    println "Waiting for running and queued builds to complete..."
    sleep(retryIntervalSec * 1000)
    retries++
}

def queueAfterWait = manager.queue

if (!isAnyJobRunningOrQueued(queueAfterWait)) {
    // Your cleanup logic here
    println "Running cleanup job on master node..."
    println "List current files in /tmp folder"
    println runCommand("ls -ltrh /tmp/")
    println "-------------------------------------------------------------------------------------------------------------"

    def diskSpaceBeforeCleanup = runCommand("ls -ltrh /tmp | head -n 1")
    println "TMP folder disk space before cleanup is $diskSpaceBeforeCleanup"

    // runCommand("rm -rf /tmp/<files>")
    println "-------------------------------------------------------------------------------------------------------------"

    def diskSpaceAfterCleanup = runCommand("ls -ltrh /tmp | head -n 1")
    println "TMP folder disk space after cleanup is $diskSpaceAfterCleanup"
    println "-------------------------------------------------------------------------------------------------------------"
} else {
    println "Skipping cleanup as other Jenkins jobs are running or queued."

    println "Running builds:"
    if (runningBuilds.isEmpty()) {
        println "No running builds."
    } else {
        println runningBuilds.join('\n')
    }

    println "Queued builds:"
    if (queuedBuilds.isEmpty()) {
        println "No queued builds."
    } else {
        println queuedBuilds.join('\n')
    }
}

def runCommand(String command) {
    def process = command.execute()
    process.waitFor()
    return process.text.trim()
}

def isAnyJobRunningOrQueued(queue) {
    // Check running builds on all computers
    for (computer in manager.computers) {
        for (executor in computer.executors) {
            if (executor.isBusy()) {
                return true
            }
        }
    }

    // Check queued builds
    if (queue.items.size() > 0) {
        return true
    }

    return false
}

尝试的修改及新报错

尝试改用Jenkins.instance替代Hudson.instance,甚至直接使用Jenkins.instance.queue,修改片段如下:

def jenkins = Jenkins.instance

def queue = jenkins.queue

// 或直接写
def queue = Jenkins.instance.queue

但出现新报错:

groovy.lang.MissingPropertyException: No such property: Jenkins for class: Script1
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:66)
    at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:51)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:310)
    at Script1.run(Script1.groovy:6)

解决方案

1. 导入正确的Jenkins核心类

Jenkins类属于jenkins.model包,而非hudson.model,需添加对应导入语句:

import jenkins.model.Jenkins
import hudson.model.Queue

新版本Jenkins已全面迁移到Jenkins类,老版本兼容的Hudson不再推荐使用。

2. 修复变量作用域问题

原脚本中isAnyJobRunningOrQueued方法直接引用外层manager变量,Groovy脚本中方法访问外层变量存在作用域限制。修改方法为接收Jenkins实例参数,避免依赖外层变量:

def isAnyJobRunningOrQueued(Jenkins jenkinsInstance, Queue queue) {
    // 检查运行中的任务,排除当前清理任务
    for (computer in jenkinsInstance.computers) {
        for (executor in computer.executors) {
            if (executor.isBusy()) {
                def currentBuildUrl = executor.currentExecutable.url
                if (!currentBuildUrl.matches('.*/clean_test/\\d+/')) {
                    return true
                }
            }
        }
    }

    // 检查排队中的任务,排除当前清理任务
    return queue.items.any { !it.task.url.contains('clean_test') }
}

3. 修正语法错误

原脚本中if (!currentBuildUrl.matches(...))未加大括号,导致只有第一行属于if块,println语句会无条件执行,修正后:

if (!currentBuildUrl.matches('.*/clean_test/\\d+/')) {
    runningBuilds << currentBuildUrl
    println "excluding current job from running builds: $currentBuildUrl"
}

4. 完整修改后的脚本

import jenkins.model.Jenkins
import hudson.model.Queue

def runningBuilds = []
def queuedBuilds = []

// 使用Jenkins.get()替代instance,更安全的单例获取方式
def jenkinsInstance = Jenkins.get()
def queue = jenkinsInstance.queue

// 过滤掉清理任务的排队项
if (queue.items.size() > 0) {
    queuedBuilds = queue.items.collect { it.task.url }.findAll { !it.contains('clean_test') }
}

// 收集非清理任务的运行项
for (computer in jenkinsInstance.computers) {
    for (executor in computer.executors) {
        if (executor.isBusy()) {
            def currentBuildUrl = executor.currentExecutable.url
            if (!currentBuildUrl.matches('.*/clean_test/\\d+/')) {
                runningBuilds << currentBuildUrl
                println "excluding current job from running builds: $currentBuildUrl"
            }
        }
    }
}

def maxRetries = 70
def retryIntervalSec = 10

def retries = 0
while (retries < maxRetries) {
    // 每次循环重新检查任务状态,避免列表过期
    def currentRunning = []
    def currentQueued = []

    for (computer in jenkinsInstance.computers) {
        for (executor in computer.executors) {
            if (executor.isBusy()) {
                def url = executor.currentExecutable.url
                if (!url.matches('.*/clean_test/\\d+/')) {
                    currentRunning << url
                }
            }
        }
    }

    currentQueued = jenkinsInstance.queue.items.collect { it.task.url }.findAll { !it.contains('clean_test') }

    if (currentRunning.isEmpty() && currentQueued.isEmpty()) {
        break
    }

    if (retries == maxRetries - 1) {
        println "Max retries reached. Exiting..."
        return
    }

    println "Waiting for running and queued builds to complete..."
    sleep(retryIntervalSec * 1000)
    retries++
}

// 最终确认无其他任务后执行清理
if (!isAnyJobRunningOrQueued(jenkinsInstance, jenkinsInstance.queue)) {
    println "Running cleanup job on master node..."
    println "List current files in /tmp folder"
    println runCommand("ls -ltrh /tmp/")
    println "-------------------------------------------------------------------------------------------------------------"

    // 用df命令更准确获取磁盘空间,替代原ls方式
    def diskSpaceBeforeCleanup = runCommand("df -h /tmp | grep -v Filesystem")
    println "TMP folder disk space before cleanup: $diskSpaceBeforeCleanup"

    // 执行清理命令,取消注释下方代码
    // runCommand("rm -rf /tmp/<your-target-files>")
    println "-------------------------------------------------------------------------------------------------------------"

    def diskSpaceAfterCleanup = runCommand("df -h /tmp | grep -v Filesystem")
    println "TMP folder disk space after cleanup: $diskSpaceAfterCleanup"
    println "-------------------------------------------------------------------------------------------------------------"
} else {
    println "Skipping cleanup as other Jenkins jobs are running or queued."

    println "Running builds:"
    def currentRunning = []
    for (computer in jenkinsInstance.computers) {
        for (executor in computer.executors) {
            if (executor.isBusy()) {
                def url = executor.currentExecutable.url
                if (!url.matches('.*/clean_test/\\d+/')) {
                    currentRunning << url
                }
            }
        }
    }
    if (currentRunning.isEmpty()) {
        println "No running builds."
    } else {
        println currentRunning.join('\n')
    }

    println "Queued builds:"
    def currentQueued = jenkinsInstance.queue.items.collect { it.task.url }.findAll { !it.contains('clean_test') }
    if (currentQueued.isEmpty()) {
        println "No queued builds."
    } else {
        println currentQueued.join('\n')
    }
}

def runCommand(String command) {
    def process = command.execute()
    process.waitFor()
    return process.text.trim()
}

def isAnyJobRunningOrQueued(Jenkins jenkinsInstance, Queue queue) {
    for (computer in jenkinsInstance.computers) {
        for (executor in computer.executors) {
            if (executor.isBusy()) {
                def currentBuildUrl = executor.currentExecutable.url
                if (!currentBuildUrl.matches('.*/clean_test/\\d+/')) {
                    return true
                }
            }
        }
    }

    return queue.items.any { !it.task.url.contains('clean_test') }
}

5. 额外注意事项

  • 脚本权限授权:在Jenkins后台进入Manage Jenkins > In-process Script Approval,批准脚本中用到的方法签名(如Jenkins.get()、Queue.items等),否则会触发权限拦截。
  • 实例获取方式:使用Jenkins.get()替代Jenkins.instance,前者是官方推荐的单例获取方式,可避免空指针异常。
  • 磁盘空间检测:原脚本用ls获取磁盘空间不准确,改用df -h /tmp能真实反映分区使用情况。

内容的提问来源于stack exchange,提问作者Adithi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 21:32:01