
Dhruvakumar Jonnagaddala
This guide provides practical guidance and examples for writing cross-platform Jenkins Pipelines that avoid sandbox approval issues and remain maintainable.
When you're shipping the same pipeline across Linux and Windows agents, the smallest thing—like a path separator; can trip you up. If you try to "fix it in Groovy" with new File(...), hudson.FilePath, or static calls like File.separator, Jenkins' Groovy Sandbox will often block those calls by default. That's on purpose and for good reasons.
This guide provides practical guidance and examples for writing cross-platform Jenkins Pipelines that avoid sandbox approval issues and remain maintainable. We avoid any methods that require @NonCPS because its presence makes the pipeline un-serializable. If Jenkins tries to pause or restart during that method, it cannot resume from the middle; it has to re-run it.
Script Security: Jenkins runs untrusted Pipeline Groovy in a restricted sandbox. Calls to java.io.File or hudson.FilePath are blocked unless approved by an admin.
FilePath is powerful: It can run remote operations on agents or controllers—too risky to allow in untrusted Pipelines.
sh / bat are whitelisted: These steps spawn external processes on the agent and don't give Groovy access to Jenkins internals.
Treat Pipelines as ephemeral– avoid persisting state.
Prefer pipeline steps over APIs (findFiles, readFile, writeFile).
Normalize paths only when needed (before sh /bat).
Use shared libraries to abstract complexity.
Handle secrets safely with withCredentials.
def sep = isUnix() ? '/' : '\\'
def dmPath = "${env.WORKSPACE}${sep}dev-tools${sep}dependency-managers"
def binPath = "${dmPath}${sep}bin"findFilesdef matches = findFiles(glob: '**/bin/**')
def binDirs = matches.collect { it.path.replaceFirst(/\/bin\/.*$/, '/bin') }.unique()
def absPath = isUnix() ? "${env.WORKSPACE}/${binDirs[0]}" : ("${env.WORKSPACE}/${binDirs[0]}").replace('/', '\\')if (isUnix()) {
sh """
"${absPath}/mytool" arg1
"""
} else {
bat """
"${absPath}\\mytool.exe" arg1
"""
}vars/osPath.groovy
def join(Object... parts) {def sep = isUnix() ? '/' : '\\'parts.findAll { it }.join(sep)}
Usage
def binAbs = osPath.join(env.WORKSPACE, "tools", "bin")Keep Groovy minimal and sandbox-friendly. Use findFiles + isUnix() for paths, normalize before calling sh/bat, and use a shared library when it gets repetitive. This ensures your pipelines remain portable, safe, and maintainable.
Customer proof