Skip to content

Instantly share code, notes, and snippets.

@Iboatwright
Last active June 20, 2022 20:52
Show Gist options
  • Select an option

  • Save Iboatwright/ae2d7f12386e55d52ec3b8fdd209b68a to your computer and use it in GitHub Desktop.

Select an option

Save Iboatwright/ae2d7f12386e55d52ec3b8fdd209b68a to your computer and use it in GitHub Desktop.
Various Jenkins Groovy shell scripts and scraps

The Jenkins server these are written for hasn't (can't) been updated since 2017. So some commands are probably deprecated in current versions.

These scripts are all written to run from the /jenkins/script Script Console.

/* ********************************************************************************************************
* This is the working script from 3/12/20@0049.
* I'm working on classing this up. For now there's basically two operations.
* getFreespace -- no parameter method that prints the date and the results of "df -h"
* cleaner -- takes the Jenkins job name, the filename portions before the counter, after,
* and the map key of the pattern to get the counter.
* >> cleaner deletes matching files from the job's build folders except for the highest counter file.
* it also skips false matches (if the filename has an extra prefix or suffix) and no match files.
* Note: it does not delete the workspace folder yet. You'll have to do that manually.
/* ********************************************************************************************************/
def getFreespace(){println "date".execute().text; println "df -h".execute().text;}
def doit(java.lang.Process one){
pone=one; pone.waitFor(); if (pone.exitValue()) { println pone.err.text } else { println pone.text };
}
def proc(String string) { string.execute() }
def ls(String path){ proc("ls ${path}") }
def lsa(String path){ proc("ls -lA ${path}") }
def rm(String path){ proc("rm ${path}") }
def rem(def nums, String path, String prefix, String suffix, def build){
println("\n[Build ${build}] ${path}");doit(lsa(path));
if( nums.size()>1 ){ nums.each{ doit(rm(path+prefix+it+suffix)) } }
doit(lsa(path));
}
def getBuildDirNums(def path){
lsa(path).text.split(/\r?\n/).findAll{it.find(~/(?:^d.*)\s([0-9]+)$/)}.collect{it.split(/\s/)[-1].toInteger()}.sort()
}
def getFileNums(def path, def pattern){
ls(path).text.findAll(pattern).collect{it.split('-')[1].toInteger()}.sort();
}
def cleaner(def job, def prefix, def suffix, def numPattern){
def jhome = "/opt/bitnami/apps/jenkins/jenkins_home/";
def b = [jhome,"jobs/",job,"/builds/",null,"/archive/target/"];
def ws = [jhome,"workspace/",job,"/target/"]
def fp = [branch:~/(?:master-)([0-9]+)/,version:~/(?:1.0.0-)([0-9]+)/,bdir:~/(?:^d.*)\s([0-9]+)$/,newline:~/\r?\n/];
def builds = b[0..3].join(); def bdirs = getBuildDirNums(builds);
bdirs.each{ def files = ls(builds+it+b[-1]).text; if (files) {
def n = getFileNums(builds+it+b[-1],fp.version); n?.pop();
rem(n, builds+it+b[-1], prefix, suffix, it)}
}
}
// -------------------- variables ---------------------------- //
def job = "deploy-X-v1"
def prefix = "X"+"-1.0.0-master-";def suffix = ".jar";
cleaner(job, prefix, suffix, "fp.pattern.name")
//getFreespace()
#!/bin/sh
# This was created to deal with existing legacy issues. It's run after the Copy-Artifact-Plugin copies a stack of artifacts from a build(dev) pipeline. It copies the highest build number (artifacts are versioned with BRANCH-pomversion-BUILD_NUMBER) artifact to a statical filename. Then further promotions only need to deal with that static filename. The pom.xml has the proper version number for this specific build.
cp $(find ./target -maxdepth 1 -regex ".+-$(ls target/*.jar | perl -n -e'/.+-(\d+)-.+/ && print "$1\n"' | sort -r -n | head -n1)-.+\.jar" -printf 'target/%P' -quit) target/last_successful_app.jar
// not sure where this came from either
import hudson.node_monitors.*
import hudson.slaves.*
import Jenkins.*
nByte = 0
nByteDir = 0
jenkins = Jenkins.instance
for (slave in jenkins.nodes) {
wsRoot = slave.getWorkspaceRoot()
println( "Nodename: " + slave.getNodeName() )
for (dir in wsRoot.list()) {
nByteDir = 0
println("=====================================================================")
println(" dirname: " + dir )
showFiles( dir )
println(" Byte Dir: " + nByteDir )
}
println("=====================================================================")
println("Byte Total: " + nByte )
println("#####################################################################")
}
def showFiles( dir ) {
for ( files in dir.list() ){
if( files.isDirectory() ) {
showFiles( files );
} else {
ByteCounter( files.length() )
}
}
}
def ByteCounter ( c )
{
nByte = nByte + c
nByteDir = nByteDir + c
}
//no comment
J=[JENKINS_HOME:'/opt/bitnami/apps/jenkins/jenkins_home',
workspace: 'workspace',
builds: 'builds',
archive: 'archive',
target: 'target',
jobs: 'jobs']
p=J.values()
String sep='/'
JOBS=['build-test-template','deploy-qa-template','deploy-uat-template-v1']
String WORKSPACE=J.JENKINS_HOME+sep+J.workspace+sep+JOBS[0]+sep
String jobs=J.JENKINS_HOME+sep+J.jobs+sep
String mjp=jobs+JOBS[0]+sep
String mws=WORKSPACE
String pathx=mws+p[2]
String pathy=mjp+p[1..2].join('/')
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//this
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//does
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//nothing
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TODO: rewrite this to handle any number of processes
// I don't take credit for the core function here, I think I found it on reddit
def doit(java.lang.Process one){
pone=one
pone.waitFor()
if (pone.exitValue()) { println pone.err.text } else { println pone.text }
}
//doit(lsa('~/.m2/repository'), grep('^d'))
def doit(java.lang.Process pone, java.lang.Process ptwo){
pone | ptwo
ptwo.waitFor()
if (ptwo.exitValue()) { println ptwo.err.text } else { println ptwo.text }
}
def doit(java.lang.Process pone, java.lang.Process ptwo, java.lang.Process pfour){
pone | ptwo | pfour
pfour.waitFor()
if (pfour.exitValue()) { println pfour.err.text } else { return pfour }
}
def doit(java.lang.Process pone, java.lang.Process ptwo, java.lang.Process pthree, java.lang.Process pfour){
pone | ptwo | pthree | pfour
pfour.waitFor()
if (pfour.exitValue()) { println pfour.err.text } else { println pfour.text }
}
//pulled from a couple different windows I had open
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
s=sed(term="total\\s([0-9]+)M", sub="\\1", g='g')
doit(lsahr(tx), head(200))
//doit(lsah(mws),wc())
//doit(lsahr(tx), egrep("^total.*M"), s)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
four=doit(lsahr(tx), egrep("^total.*M"), s)
acc = 0
fs = four.text.split()
//print fs
//fs.each { acc += it as Integer }
//println "total ${acc}M"
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
duh=du(tx)
g=grep('^[0-9]*\\.[0-9]*M')
//doit(duh, g, sort('-nr'), head(30))
// utility one-liners mainly used with doit()
def proc(String string) { string.execute() }
def awk(String cmd){ proc("awk ${cmd}") }
def du(String path){ proc("du -h ${path}") }
def lsa(String path){ proc("ls -ltA ${path}") }
def lsah(String path){ proc("ls -lthrA ${path}") }
def lsahr(String path){ proc("ls -lthrAR ${path}") }
def head(int lines){ proc("head -n${lines}") }
def head(int lines, String filePath){ proc("head -n${lines} ${filePath}") }
def tail(int lines){ proc("tail -n${lines}") }
def tail(int lines, String filePath){ proc("tail -n${lines} ${filePath}") }
def cat(String filePath){ proc("cat ${filePath}") }
def find(String cmd){ proc("find ${cmd}") }
def efind(String opts='./*', String exec){ proc("find ${opts} -exec ${exec} ;") }
def find2(String path='./*', String opts){ proc("find ${path} -maxdepth 3 ${opts} -printf '%P\\n'") }
def find3(String path='.', String opts='', String pattern){ proc("find ${path} ${opts} ${pattern} -printf '%P\\n'") }
def rm(String path){ proc("rm ${path}") } //rm(cpath)
def rmf(String path, String R=''){ proc("rm -f${R} ${path}") } //rmf(cpath, 'R')
def grep(String pattern){ proc("grep \"${pattern}\"") }
def egrep(String pattern){ proc("grep -E ${pattern}") }
def cp(String src, String tgt=".") { proc("cp ${src} ${tgt}") }
def sort(String opts){ proc("sort ${opts}") }
def sed(String term, String sub='', String g=''){ proc("sed -E s,${term},${sub},${g}") }
def wc(){ proc("wc -l") }
// The original version of this script is found here:
// ref: https://support.cloudbees.com/hc/en-us/articles/215549798-Best-Strategy-for-Disk-Space-Management-Clean-Up-Old-Builds
// --
// String dryRun: to only list the jobs which would be changed
// int daysToKeep: If not -1, history is only kept up to this day.
// int numToKeep: If not -1, only this number of build logs are kept.
// int artifactDaysToKeep: If not -1, artifacts are only kept up to this day.
// int artifactNumToKeep: If not -1, only this number of builds have their artifacts kept.
dryRun="!true"
daysToKeep = -1
numToKeep = -1
artifactDaysToKeep = -1
artifactNumToKeep = 5
LinkedList builds = []
LinkedList nevers = []
def rotatorStatus = [:]
Jenkins.instance.getAllItems(Job).findAll{ !it.logRotator }.each { job ->
if (job.isBuildable()
&& job.supportsLogRotator()
&& job.getProperty(jenkins.model.BuildDiscarderProperty) == null
&& !(job instanceof org.jenkinsci.plugins.workflow.job.WorkflowJob) ) // toggle to process workflow jobs, other jobs or all jobs
{
(job.getLastBuild()) ? builds.add(job) : nevers.add(job)
if (!"true".equals(dryRun)) {
// adding a property implicitly saves so no explicit one
try {
job.setBuildDiscarder(new hudson.tasks.LogRotator ( daysToKeep, numToKeep, artifactDaysToKeep, artifactNumToKeep))
rotatorStatus.put(job, "pass")
} catch (Exception e) {
// Some implementation like for example the hudson.matrix.MatrixConfiguration supports a LogRotator but not setting it
println "[WARNING] Failed to update ${job.fullName} of type ${job.class} : ${e}"
rotatorStatus.put(job, "fail")
}
}
}
}
builds.sort({a,b -> b.getLastBuild().getTimeInMillis() <=> a.getLastBuild().getTimeInMillis()}).each {
println "[${rotatorStatus[it]}] ${it.getLastBuild().getTime()} -- ${it.fullDisplayName}"
}
println " - - - - - - - - - - - -"
nevers.each { println "[${rotatorStatus[it]}] Never built once -- ${it.fullDisplayName}"}
println "~fin~"
// this prints all the parameters for a job using a Jenkins API call
// not sure where I copied this from
import groovy.json.JsonSlurper
def root = "<url to job>"
def options = "/api/json?tree=builds[actions[parameters[name,value]],result,building,number,duration,estimatedDuration]"
def jsonSlurper = new JsonSlurper()
def text = new URL("${root}/${options}").text
def data = jsonSlurper.parseText(text)
data["builds"].each { buildsdata ->
def result = buildsdata["result"]
def num = buildsdata["number"]
print("${root}/${num}/parameters |")
buildsdata["actions"].each { actions ->
if (actions["_class"].equals("hudson.model.ParametersAction")) {
actions["parameters"].sort({it.name}).each { param ->
print("${param.name}=${param.value}|")
}
}
}
println("")
}
---------------------------------------------------------------------------------------------------------
// get list of disabled jobs (I think...)
jenkins.model.Jenkins.instance.getAllItems(jenkins.model.ParameterizedJobMixIn.ParameterizedJob.class)
.findAll{job -> !(job.metaClass.methods*.name.findAll{method -> method == "isDisabled"}.isEmpty()) }
.findAll{job -> job.disabled}
.each{job ->
def className = job.getClass();
def lastBuild = job.getLastBuild()? job.getLastBuild().number: "Never built once";
def lastBuildOnDate = job.getLastBuild()? job.getLastBuild().getTime():"Never built once"
def url = job.getUrl()
println "$url~$lastBuild~$lastBuildOnDate~$job.disabled"
}
// These are some snippets that I plan to convert to one or two Jenkins jobs to help enforce our log-rotation policy
// This will run log rotate on all Jenkins Jobs that have a log rotate stage/step defined
Jenkins.instance.getAllItems(Job).findAll{ it.logRotator }.each {
(it instanceof org.jenkinsci.plugins.workflow.job.WorkflowJob) && it.logRotator.perform(it)
};
// I run this before and after any pruning actions to know how much space I've freed up
// filename: getFreeSpace.groovy
println "date".execute().text
println "df -h".execute().text
// test script while I was trying to get the cloudbees.folder nested jobs
LinkedList builds = []
LinkedList nevers = []
job = Jenkins.instance.getItemByFullName("cloudbeesFolder/deploy-uat-template-v1")
println "${job.name} of type ${job.class}"
if (false && job.isBuildable()
&& job.supportsLogRotator()
&& job.getProperty(jenkins.model.BuildDiscarderProperty) == null
&& !(job instanceof org.jenkinsci.plugins.workflow.job.WorkflowJob) ) {
println "no rotator set for \"${job.fullDisplayName}\" -- " + job.getClass()
try {
//job.setBuildDiscarder(new hudson.tasks.LogRotator ( daysToKeep, numToKeep, artifactDaysToKeep, artifactNumToKeep))
} catch (Exception e) {
println "[WARNING] Failed to update ${job.fullName} of type ${job.class} : ${e}"
}
} else {
builds.add(job)
println "${builds[0].fullDisplayName} has a rotator already. lenth = ${builds.size()}"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment