[!TIP] to list methods on a class instance:
Copy thing.metaClass.methods*.name.sort().unique()
to determine a class from an instance:
Copy thing.class
// or
thing.getClass()
usage
[!TIP]
CSRF Protection Explained
if you authenticate your API calls with a username and a user API token then a crumb is not required from Jenkins 2.96
to get CSRF crumb via curl
Copy $ SERVER="https://localhost:8080"
$ COOKIEJAR="$(mktemp)"
$ CRUMB=$(curl -u "admin:admin" -s --cookie-jar "$COOKIEJAR" "$SERVER/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,%22:%22,//crumb)")
# to run script via curl
$ curl -d "script=System.getProperties()" \
-u "admin:admin" \
--cookie "$COOKIEJAR" \
-H "$CRUMB" \
https://${SERVER}/scriptText
or
Copy $ SERVER="https://localhost:8080"
$ COOKIEJAR="$(mktemp)"
$ CRUMB=$(curl -u "admin:admin" \
--cookie-jar "${COOKIEJAR}" \
'https://${SERVER}/crumbIssuer/api/json' |
jq -r '[.crumbRequestField, .crumb] | join(":")'
)
# verify
$ echo $CRUMB
Jenkins-Crumb:c11dc*******************************************************e463
$ curl -d "script=System.getProperties()" \
-u "admin:admin" \
-s \
--cookie "$COOKIEJAR" \
-H "$CRUMB" \
https://${SERVER}/scriptText
$ curl --data-urlencode "script=$(< ./script.groovy)" \
-s \
--netrc-file ~/.netrc \
--cookie "${COOKIEJAR}" \
-H "${CRUMB}" \
https://${SERVER}/scriptText
remote access
Copy $ curl -d "script=<your_script_here>" https://jenkins/script
# or to get output as a plain text result (no HTML)
$ curl -d "script=<your_script_here>" https://jenkins/scriptText
curl submitting groovy file
Copy $ curl --data-urlencode "script=$(< ./somescript.groovy)" https://jenkins/scriptText
via api token
Copy $ curl --user 'username:api-token' \
--data-urlencode \
"script=$(< ./somescript.groovy)" \
https://jenkins/scriptText
via python
Copy with open('somescript.groovy', 'r') as fd:
data = fd.read()
r = requests.post('https://jenkins/scriptText', auth=('username', 'api-token'), data={'script': data})
setup system property (temporary)
[!TIP|label:references:]
timestampe
Copy System.setProperty( 'org.apache.commons.jelly.tags.fmt.timeZone', 'America/Los_Angeles' )
shell step aborts
Copy System.setProperty( 'org.jenkinsci.plugins.durabletask.BourneShellScript.HEARTBEAT_CHECK_INTERVAL', 36000 )
to clear property
Copy System.clearProperty( 'hudson.model.DirectoryBrowserSupport.CSP' )
to get property
Copy System.getProperty( 'hudson.model.DirectoryBrowserSupport.CSP' )
to get all properties
Copy System.getProperties()
// or
System.getProperties().sort().collectEntries{[ (it.key), (it.value) ]}
System.getProperties().sort().each { println "${it.key} ~> ${it.value}" }
System.getProperties().sort().collect{ "${it.key} ~> ${it.value}" }.join('\n')
extend built-in node executor
Copy import jenkins.model.*
jenkins.model.Jenkins.instance.setNumExecutors(5)
execute shell script in console
[!TIP|label:references:]
Copy println ( 'uname -a'.execute().text )
// or
println ( 'printenv'.execute().in.text )
result
Copy Linux devops-jenkins-685cf57df9-znfs8 4.19.12-1.el7.elrepo.x86_64 #1 SMP Fri Dec 21 11:06:36 EST 2018 x86_64 GNU/Linux
or
Copy import hudson.util.RemotingDiagnostics
import jenkins.model.Jenkins
String agentName = 'your agent name'
// groovy script you want executed on an agent
groovyScript = '''
println System.getenv("PATH")
println "uname -a".execute().text
'''.stripIndent()
String result
jenkins.model.Jenkins.instance.slaves.find { agent ->
agent.name == agentName
}.with { agent ->
result = RemotingDiagnostics.executeGroovy( groovyScript, agent.channel )
}
println result
read & write files
Copy // write
new File('/tmp/file.txt').withWriter('UTF-8') { writer ->
try {
writer << 'hello world\n'
} finally {
writer.close()
}
}
// read
new File('/tmp/file.txt').text
write file in agent
Copy import hudson.FilePath
import hudson.remoting.Channel
import jenkins.model.Jenkins
String agentName = 'some-agent'
String filePath = '/tmp/file.txt'
Channel agentChannel = jenkins.model.Jenkins.instance.slaves.find { agent ->
agent.name == agentName
}.channel
new FilePath( agentChannel, filePath ).write().with { os ->
try {
os << 'hello world\n'
} finally {
os.close()
}
}
read file from an agent
Copy import hudson.FilePath
import hudson.remoting.Channel
import jenkins.model.Jenkins
import java.io.BufferedReader
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
import java.util.stream.Collectors
String agentName = 'some-agent'
String filePath = '/tmp/file.txt'
Channel agentChannel = jenkins.model.Jenkins.instance.slaves.find { it.name == agentName }.channel
String fileContents = ''
new FilePath(agentChannel, filePath).read().with { is ->
try {
fileContents = new BufferedReader(
new InputStreamReader(is, StandardCharsets.UTF_8))
.lines()
.collect(Collectors.joining("\n")
)
} finally {
is.close()
}
} // with
// print contents of the file from the agent
println '==='
println(fileContents)
println '==='
nslookup
[!NOTE|label:references:]
Copy if( !binding.hasVariable('domain') ) {
domain = 'example.com'
}
if( !(domain in String) ) {
throw new Exception('PARAMETER ERROR: domain must be a string.')
}
InetAddress dnsInetAddress = InetAddress.getByName domain
println dnsInetAddress.hostAddress
jenkins system
Copy import jenkins.model.*;
import org.jenkinsci.main.modules.sshd.*;
jenins.model.Jenkins instance = jenkins.model.Jenkins.instance
instance.setDisableRememberMe( false )
instance.setNumExecutors( 2 )
instance.setSystemMessage( '<h2>welcome to the Jenkins Master</h2>' )
instance.setRawBuildsDir()
instance.save()
def sshd = SSHD.get()
sshd.setPort( 12345 )
sshd.save()
Copy println( "Groovy: ${GroovySystem.version}" )
println( "Jenkins: ${jenkins.model.Jenkins.instance.getVersion()}" )
println( "OS: ${System.getProperty('os.name')} - ${System.getProperty('os.version')}" )
println( "Java: ${System.getProperty('java.version')} - ${System.getProperty('java.vm.vendor')} (${System.getProperty('java.vm.name')})" )
println "---"
jenkins.model.Jenkins.instance.pluginManager.plugins
.collect()
.sort { it.getShortName() }
.each {
plugin -> println("${plugin.getShortName()}:${plugin.getVersion()}")
}
return
modify log level
Copy System.setProperty( 'org.apache.commons.logging.Log', 'org.apache.commons.logging.impl.SimpleLog' );
System.setProperty( 'org.apache.commons.logging.simplelog.showdatetime', 'true' );
System.setProperty( 'org.apache.commons.logging.simplelog.log.httpclient.wire.header', 'error' );
System.setProperty( 'org.apache.commons.logging.simplelog.log.org.apache.http', 'error' );
System.setProperty( 'log4j.logger.org.apache.http', 'error' );
System.setProperty( 'log4j.logger.org.apache.http.wire', 'error' );
System.setProperty( 'org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient', 'error' );
check log level
Copy println System.getProperty( 'org.apache.commons.logging.Log' );
println System.getProperty( 'org.apache.commons.logging.simplelog.showdatetime' );
println System.getProperty( 'org.apache.commons.logging.simplelog.log.httpclient.wire.header' );
println System.getProperty( 'org.apache.commons.logging.simplelog.log.org.apache.http' );
println System.getProperty( 'log4j.logger.org.apache.http' );
println System.getProperty( 'log4j.logger.org.apache.http.wire' );
println System.getProperty( 'org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient' );
theme management
Copy import jenkins.model.*
def theme = null
// Check if Simple Theme Plugin is installed
def themeManager = Jenkins.instance.getExtensionList('org.codefirst.SimpleThemeDecorator')
if (themeManager && themeManager.size() > 0) {
theme = themeManager[0].getUrl()
println( "Current theme URL: ${theme}" )
} else {
println("No theme plugin detected, using default Jenkins theme.")
}
// Example logic to infer Dark Theme
if (theme && theme.toLowerCase().contains("dark")) {
println("Jenkins is using a Dark Theme.")
} else {
println("Jenkins is not using a Dark Theme.")
}
RABC
[!NOTE|label:references:]
jobs & builds
[!TIP|label:get more:]
list build status with percentage
get all builds status during certain start-end time
Copy hudson.FilePath workspace = hudson.model.Executor.currentExecutor().getCurrentWorkspace()
get absolute path
Copy println( "script directory: ${new File(__FILE__).parent.absolutePath}" )
Copy // you have to install the Shelve Project Plugin on your Jenkins Master
// the maximum value for daysBack is 365, going beyond 365 will break the script.
import org.jvnet.hudson.plugins.shelveproject.ShelveProjectTask
def daysBack = 365;
jenkins.model.Jenkins.instance.getAllItems( AbstractProject.class ).each { it ->
def lastBuild = it.getLastBuild()
if( lastBuild != null ) {
def back = Calendar.getInstance()
back.set( Calendar.DAY_OF_YEAR,back.get(Calendar.DAY_OF_YEAR) - daysBack )
if ( lastBuild.getTime().compareTo(back.getTime()) < 0 ) {
println it.name + " was built over " + daysBack + " days ago: " + lastBuild.getTime()
if ( it instanceof AbstractProject ){
def spt= new ShelveProjectTask(it)
Hudson.getInstance().getQueue().schedule(spt , 0 );
} else {
println it.name + " was not shelved ----------- "
}
}
}
}
plugins
Copy jenkins.model.Jenkins.instance
.pluginManager
.plugins
.each { plugin ->
println ( "${plugin.getDisplayName()} (${plugin.getShortName()}): ${plugin.getVersion()}" )
}
list for helm-value.yaml
Copy jenkins.model.Jenkins.instance
.pluginManager
.plugins
.sort(false) { a, b ->
a.getShortName().toLowerCase() <=> b.getShortName().toLowerCase()
}
.each { plugin ->
println "- ${plugin.getShortName()}:${plugin.getVersion()}"
}
"DONE"
Copy ExtensionList.lookup( UnprotectedRootAction ).each {
println String.format( "URL: '%s/' provided by '%s' in '%s'",
it.urlName,
Jenkins.get().pluginManager.whichPlugin(it.class)?.shortName?:"Jenkins Core",
it.class.name
)
}
list plugin and dependencies
Copy println jenkins.model.Jenkins.instance.pluginManager.plugins
.sort(false) { a, b ->
a.getShortName().toLowerCase() <=> b.getShortName().toLowerCase()
}
.collect { plugin ->
"~~> ${plugin.shortName} : ${plugin.version} : ${plugin.displayName}" +
( plugin.dependants ? "\n\t+++ ${plugin.dependants.join('\n\t+++ ')}" : '' ) +
( plugin.dependencies ? "\n\t... ${plugin.dependencies.join('\n\t... ')}" : '' )
}
.join('\n')
get specific plugin dependencies
Copy List<String> keywords = [ 'jsch' ]
println jenkins.model.Jenkins.instance.pluginManager.plugins
.findAll { plugin -> keywords.any { it == plugin.shortName } }
.sort(false) { a, b ->
a.getShortName().toLowerCase() <=> b.getShortName().toLowerCase()
}
.collect { plugin ->
"~~> ${plugin.shortName} : ${plugin.version} : ${plugin.displayName}" +
( plugin.dependants ? "\n\t+++ ${plugin.dependants.join('\n\t+++ ')}" : '' ) +
( plugin.dependencies ? "\n\t... ${plugin.dependencies.join('\n\t... ')}" : '' )
}
.join('\n')
get dependency tree
Copy import hudson.PluginWrapper
def getDependencyTree( String keyword, Integer benchmark = 2, Integer index = 0 ) {
String prefix = index ? '\t' + "|\t"*(index-1) + "|... " : ''
PluginWrapper plugin = jenkins.model.Jenkins.instance.pluginManager.plugins.find { keyword == it.shortName }
List dependencies = plugin.collect { it.dependencies }.flatten() ?: []
println prefix + "${plugin.shortName} ( ${plugin.version} )"
if ( dependencies && benchmark != index ) {
dependencies.collect{ it.shortName }.each { getDependencyTree (it, benchmark, index+1) }
}
}
getDependencyTree( 'jsch', 100 )
"DONE"
result
Copy jsch ( 0.2.8-65.v052c39de79b_2 )
|... ssh-credentials ( 305.v8f4381501156 )
| |... credentials ( 1254.vb_96f366e7b_a_d )
| | |... structs ( 324.va_f5d6774f3a_d )
| | | |... javax-activation-api ( 1.2.0-6 )
| | | |... javax-mail-api ( 1.6.2-9 )
| | | | |... javax-activation-api ( 1.2.0-6 )
| | | |... instance-identity ( 173.va_37c494ec4e5 )
| | | | |... bouncycastle-api ( 2.28 )
| | |... configuration-as-code ( 1647.ve39ca_b_829b_42 )
| | | |... caffeine-api ( 3.1.6-115.vb_8b_b_328e59d8 )
| | | |... commons-text-api ( 1.10.0-36.vc008c8fcda_7b_ )
| | | | |... commons-lang3-api ( 3.12.0-36.vd97de6465d5b_ )
| | | | | |... javax-activation-api ( 1.2.0-6 )
| | | | | |... javax-mail-api ( 1.6.2-9 )
| | | | | | |... javax-activation-api ( 1.2.0-6 )
| | | | | |... instance-identity ( 173.va_37c494ec4e5 )
| | | | | | |... bouncycastle-api ( 2.28 )
| | | |... snakeyaml-api ( 1.33-95.va_b_a_e3e47b_fa_4 )
| |... trilead-api ( 2.84.v72119de229b_7 )
| |... instance-identity ( 173.va_37c494ec4e5 )
| | |... bouncycastle-api ( 2.28 )
|... trilead-api ( 2.84.v72119de229b_7 )
|... javax-activation-api ( 1.2.0-6 )
|... javax-mail-api ( 1.6.2-9 )
| |... javax-activation-api ( 1.2.0-6 )
|... instance-identity ( 173.va_37c494ec4e5 )
| |... bouncycastle-api ( 2.28 )
others
Copy def plugins = jenkins.model.Jenkins.instance
.pluginManager
.plugins
.sort(false) { a, b ->
a.getShortName().toLowerCase() <=> b.getShortName().toLowerCase()
}
println "jenkins instance : ${jenkins.model.Jenkins.instance.getComputer('').hostName} + ${jenkins.model.Jenkins.instance.rootUrl}\n" +
"installed plugins:\n=================="
plugins.each { plugin ->
println " ${plugin.getShortName()} : ${plugin.getVersion()} | ${plugin.getDisplayName()}"
}
println "\nplugins dependency tree (...: dependencies; +++: dependants) :\n======================="
plugins.each { plugin ->
println """
${plugin.getShortName()} : ${plugin.getVersion()} | ${plugin.getDisplayName()}
+++ ${plugin.getDependants()}
... ${plugin.getDependencies()}
"""
}
or
Copy def jenkins = jenkins.model.Jenkins.instance
println """
Jenkins Instance : ${jenkins.getComputer('').hostName} + ${jenkins.rootUrl}
Installed Plugins:
==================
"""
jenkins.pluginManager
.plugins
.sort(false) { a, b ->
a.getShortName().toLowerCase() <=> b.getShortName().toLowerCase()
}.each { plugin ->
println "${plugin.getShortName()}: ${plugin.getVersion()} | ${plugin.getDisplayName()}"
}
println """
Plugins Dependency tree (...: dependencies; +++: dependants) :
=======================
"""
jenkins.pluginManager
.plugins
.sort(false) { a, b ->
a.getShortName().toLowerCase() <=> b.getShortName().toLowerCase()
}.each { plugin ->
println """
${plugin.getShortName()} : ${plugin.getVersion()} | ${plugin.getDisplayName()}
+++ ${plugin.getDependants()}
... ${plugin.getDependencies()}
"""
}
scriptApproval
backup & restore all scriptApproval items
backup
Copy import java.lang.reflect.*
import jenkins.model.Jenkins
import jenkins.model.*
import org.jenkinsci.plugins.scriptsecurity.scripts.*
import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.*
import static groovy.json.JsonOutput.*
scriptApproval = ScriptApproval.get()
alreadyApproved = new HashSet<>(Arrays.asList(scriptApproval.getApprovedSignatures()))
println prettyPrint( toJson(alreadyApproved.sort()) )
restore
Copy def scriptApproval = org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval.get()
String[] signs = [
'method org.jenkinsci.plugins.workflow.steps.FlowInterruptedException getCauses' ,
'method org.jenkinsci.plugins.workflow.support.steps.input.Rejection getUser'
]
for( String sign : signs ) {
scriptApproval.approveSignature( sign )
}
scriptApproval.save()
example
Copy import java.lang.reflect.*;
import jenkins.model.Jenkins;
import jenkins.model.*;
import org.jenkinsci.plugins.scriptsecurity.scripts.*;
import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.*;
import static groovy.json.JsonOutput.*
ScriptApproval scriptApproval = ScriptApproval.get()
HashSet<String> alreadyApproved = new HashSet<>(Arrays.asList(scriptApproval.getApprovedSignatures()))
Closure approveSignature = { String signature ->
if ( ! alreadyApproved?.contains(signature) ) scriptApproval.approveSignature( signature )
}
[
'field org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval$PendingSignature dangerous' ,
'field org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval$PendingSignature signature' ,
'method org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval$PendingThing getContext' ,
'method org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval approveSignature java.lang.String' ,
'method org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval getPendingScripts' ,
'method org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval getPendingSignatures' ,
'staticMethod org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval get' ,
'staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods flatten java.util.Set' ,
'method org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper getRawBuild'
].each { println "~~> ${it}"; approveSignature(it) }
scriptApproval.save()
Jenkinsfile
Copy #!/usr/bin/env groovy
import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval
timestamps { ansiColor('xterm') {
def requester = currentBuild.rawBuild.getCause(UserIdCause.class)?.getUserId() ?: 'jenkins'
final List<String> description = []
try {
ScriptApproval scriptApproval = ScriptApproval.get()
final LinkedHashSet<String> pendingScripts = new HashSet<>(Arrays.asList( scriptApproval.getPendingScripts() )).flatten()
final LinkedHashSet<String> pendingSignature = new HashSet<>(Arrays.asList( scriptApproval.getPendingSignatures() )).flatten()
if ( ! pendingScripts && ! pendingSignature ) {
currentBuild.description = 'NOT_BUILT: nothing can be approved'
currentBuild.rawBuild.executor.interrupt( Result.NOT_BUILT )
}
if ( pendingScripts ) {
println 'scripts pending approval ...'
pendingScripts.collect().each { ps ->
String log = "${ps.context.user}@${ps.context.psem.fullName} : ${ps.hash} ( ${ps.language.class.simpleName} )"
description << log
println "~~> ${log}. scripts: \n ${ps.script}"
scriptApproval.approveScript( ps.hash )
}
scriptApproval.save()
} // pendingScripts
if ( pendingSignature ) {
println 'signatures pending approval ...'
pendingSignature.collect().each { ps ->
String signature = ps.signature
if ( ! ps.dangerous ) {
description << signature
println "~~> '${signature}'"
scriptApproval.approveSignature( signature )
} else {
println "~~> '${signature}' is too dangerous to be approval automatically. contact with Jenkins administrator."
}
scriptApproval.save()
}
}
} catch(e) {
def sw = new StringWriter()
e.printStackTrace( new PrintWriter(sw) )
echo sw.toString()
throw e
} finally {
if ( description ) {
currentBuild.description = "@${requesterId} " +
"${buildResults.isSuccess(currentBuild.currentResult) ? 'successful' : 'failed to'} " +
"approved : '${description.join('; ')}'"
}
} // try/catch/finally
}} // ansiColor | timestamps
// vim:tabstop=2:softtabstop=2:shiftwidth=2:expandtab:filetype=Jenkinsfile
automatic approval
Copy // libs.groovy
def autoAccept( Closure body ) {
try {
body()
} catch ( org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException e ) {
String msg = "NOT_BUILT : interrupted by approval scripts or signature"
def cause = { msg as String } as CauseOfInterruption
currentBuild.rawBuild.executor.interrupt( Result.NOT_BUILT, cause )
currentBuild.description = msg
build wait: false, job: '/marslo/scriptApproval'
}
}
// jenkinsfile
libs.autoAccept() {
...content...
}
automatic approval all pending
list pending scriptApproval
Copy import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval
ScriptApproval scriptApproval = ScriptApproval.get()
scriptApproval.pendingScripts.each { scriptApproval.approveScript( it.hash ) }
job dsl support for scriptapproval
Copy import jenkins.model.Jenkins
def scriptApproval = jenkins.model.Jenkins.instance
.getExtensionList('org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval')[0]
def hashesToApprove = scriptApproval.pendingScripts
.findAll{ it.script.startsWith(approvalPrefix) }
.collect{ it.getHash() }
hashesToApprove.each { scriptApproval.approveScript(it) }
[!NOTE|label:@deprecated]
file: $JENKINS_HOME/init.groovy.d/disable-script-security.groovy
Copy import javaposse.jobdsl.plugin.GlobalJobDslSecurityConfiguration
import jenkins.model.GlobalConfiguration
// disable Job DSL script approval
GlobalConfiguration.all().get(GlobalJobDslSecurityConfiguration.class).useScriptSecurity=false
GlobalConfiguration.all().get(GlobalJobDslSecurityConfiguration.class).save()