"cancel" variable in groovy script not working in Jenkins Email-ext plugin - groovy

I'm having an issue where I want to cancel sending an email when a job gets into the FIXED state from the UNSTABLE state. So if a job was failing but got fixed, I want to send the message. But if a job is unstable (tests not passing) and gets fixed, don't send the email. It might be that a job is marked UNSTABLE after it has been in FAILURE state. When it goes to SUCCESS (i.e. it is fixed) I want an email in that case. You can see the cases in the code below.
My problem is that when I set the variable cancel to true by definition[1] it should cancel the email, but it doesn't. The email gets sent every time. Of course I'm using triggers for "Failure", "Still failing" and "Fixed".
Jenkins version: 1.533. Email-ext version: 2.37.2.2
// The goal of this script is to block sending the 'FIXED' message
// when the status goes from 'UNSTABLE' to 'SUCCESS'
//
// These are the cases (where F=FAILURE, U=UNSTABLE, S=SUCCESS)
// S - S : no msg (previous state: S, current state: S)
// F - S : msg
// S - U ... U - S : no msg <-- this is the one we need to avoid sending an email
// F - U ... U - S : msg
logger.println("Entering pre-send script")
// variable definitions
def keepGoing= true
def cancelEmail = false
// object to current job
job = hudson.model.Hudson.instance.getItem("incr-build-master")
// current build number
buildNumber = build.getNumber()
logger.println("Current build number: " + buildNumber)
// if the build failed or is unstable don't to anything,
// the specific triggers should take care of the messages
if (build.result.toString().equals("SUCCESS"))
{
logger.println("Build is successful. Procesing...")
while( keepGoing )
{
// get the number of the next past build
pastBuild = job.getBuildByNumber(--buildNumber)
buildResult = pastBuild.result.toString()
switch ( buildResult )
{
case "SUCCESS":
// if the previous non-unstable build was successful
// don't send a 'FIXED' message
cancelEmail = true
keepGoing = false
logger.println("Cancel sending email")
break
case "FAILURE":
// here we exit, but we will send the 'FIXED' message
keepGoing = false
logger.println("Send email")
break
case "UNSTABLE":
// let us keep looking until we find a previous build
// that is either 'SUCCESS' or 'FAILURE*
logger.println("Build " + buildNumber + " is unstable")
break
default:
logger.println("Error in script: result string is wrong - " + buildResult)
return
}
}
}
logger.println("Emailed canceled?: " + cancelEmail)
cancel=cancelEmail
logger.println("Exiting pre-send script")
[1] from the Help icon: "You may also cancel sending the email by setting the boolean variable "cancel" to true."

I encountered the same problem and found solution in a couple of days.
"cancel" only works when it is used in the last line of code.
This will cancel the build:
changed = false
files = 5
cancel = true
This will not:
changed = false
cancel = true
files = 5
And this will also do cancel:
changed = false
files = 5
if (files > 2) {
cancel = true
}
I hope this will save some time for somebody.

I am having a similar problem.
In the Pre-send script I put:
if ((build.getNumber() % 2) == 0) {
cancel=true;
} else {
cancel=false;
}
logger.println("cancel = " + cancel);
I get e-mail, with the build.log file attached, that shows the "cancel = true" and "cancel = false" cases.

Related

Chrome Extension | Multiple alarms going off at once

I am creating a task reminder extension. The user has an option to keep adding tasks and set reminders for each task.
I am using chrome.storage to store these tasks and using onChanged listener on storage to create an alarm for each task added to the storage.
But the issue is that if I set a reminder of 2 mins for a task and 3 mins for another task. Then at the end of 2 mins I am getting notification for both the tasks and at the end of 3mins I again get notifications for both the tasks.
background.js
chrome.storage.onChanged.addListener(function(changes, namespace) {
let id = (changes.tasks.newValue.length)-1
let data = changes.tasks.newValue[id]
if(data.task && data.hrs && data.min){
let totalMins = (parseInt(data.hrs*60))+parseInt(data.min)
let alarmTime = 60*1000*totalMins
chrome.alarms.create("remind"+id,{when:Date.now() + alarmTime})
}
chrome.alarms.onAlarm.addListener(()=>{
let notifObj = {
type: "basic",
iconUrl:"./images/logo5.png",
title: "Time to complete you task",
message: data.task
}
chrome.notifications.create('remindNotif'+id, notifObj)
})
popup.js
let hrs = document.querySelector("#time-hrs")
let min = document.querySelector("#time-min")
let submitBtn = document.querySelector("#submitBtn")
let task = document.querySelector("#task")
hrs.value = 0;
min.value = 1
hrs.addEventListener('change',()=>{
if (hrs.value < 0){
hrs.value =0;
}
})
min.addEventListener('change',()=>{
if (min.value < 1){
min.value = 1;
}
})
submitBtn.addEventListener("click", ()=>{
if(task.value){
chrome.storage.sync.get('tasks',(item)=>{
let taskArr = item.tasks ? item.tasks : []
linkArr.push({task:task.value, hrs:hrs.value, min:min.value})
chrome.storage.sync.set({ 'tasks' : taskArr })
})
};
});
manifest.json
{
"name" : "Link Snooze",
"description" : "This extension reminds you to open your saved links",
"manifest_version":2,
"version":"0.1.0",
"icons":{
"16":"./images/logo5.png",
"48":"./images/logo5.png",
"128":"./images/logo5.png"
},
"browser_action":{
"default_popup":"popup.html",
"default_icon":"./images/logo5.png"
},
"permissions":["storage", "notifications","alarms"],
"background" : {
"scripts": ["background.js"],
"persistent" : false
},
"options_page":"options.html"
}
Problem.
You register a new onAlarms listener when the storage changes in addition to the old listeners. All of them run each time one alarm is triggered.
Solution.
When using a non-persistent background script, all API listeners must be registered just once for the same function and it must be done synchronously, not inside an asynchronous callback or await or then(), otherwise the event will be lost when the background script auto-terminates and then wakes up for this event. The convention is to do it at the beginning of the script. The reason it worked for you until now is that the background script is kept alive while the popup is open or while devtools for the background script was open.
Such listeners evidently won't be able to use the variables from an asynchronous callback directly like data.task in your code. The solution is to use a different method of attaching data to an event, for example, create the alarm with a name that already contains the data, specifically data.task.
chrome.alarms.create(data.task, {delayInMinutes: hrs * 60 + min});
onAlarm event provides the alarm as a parameter so you can use its name, see the documentation.
Random hints:
An object can be used as an alarm name if you call JSON.stringify(obj) when creating and JSON.parse(alarm.name) in onAlarm.
In the popup, instead of manually adjusting out-of-range values, use a number input in html:
<input id="time-min" type=number min=0 max=59 step=1>
Then read it as a number: document.querySelector("#time-min").valueAsNumber || 0

Search a string with Javascript

Hi everyone,
I am trying to test C programs that use an user input... Like a learning app. So the avaliator(teacher) can write tests and I compile the code with a help of a docker and get back the result of the program that I send. After that I verify if one of the case tests fails..
for that I have two strings, like this:
result = "input_compiled1540323505983: /home/compiler/input/input.c:9: main: Assertion `B==2' failed. timeout: the monitored command dumped core Aborted "
and an array with case tests that is like:
caseTests = [" assert(A==3); // A must have the value of 3;", " assert(B==2); // B must have the value of 2; ", " assert(strcmp(Fulano, "Fulano")==0); //Fulano must be equal to Fulano]
I need to send back from my server something like this:
{ console: [true, true, true ] }
Where each true is the corresponding test for every test in the array of tests
So, I need to test if one string contains the part of another string... and for now I did like this:
criandoConsole = function(arrayErros, arrayResult){
var consol = arrayErros.map( function( elem ) {
var local = elem.match(/\((.*)\)/);
if(arrayResult.indexOf(local) > -1 ) {
return false;
}
else return true;
});
return consol;
}
I am wondering if there are any more efective way of doing that. I am using a nodejs as server. Does anyone know a better way?!
ps: Just do like result.contains(caseTests[0]) did not work..
I know this is changing the problem, but can you simplify the error array to only include the search terms? For example,
result = "input_compiled1540323505983: /home/compiler/input/input.c:9: main: Assertion `B==2' failed. timeout: the monitored command dumped core Aborted ";
//simplify the search patterns
caseTests = [
"A==3",
"B==2",
"strcmp(Fulano, \"Fulano\")==0"
]
criandoConsole = function(arrayErros, arrayResult){
var consol = arrayErros.map( function( elem ) {
if (arrayResult.indexOf(elem) != -1)
return false; //assert failed?
else
return true; //success?
});
return consol;
}
console.log(criandoConsole(caseTests,result));

jenkins Slave API setLabelString adds to User list

I have the following script under a JOB, which when run adds the userId to the label of the slave.
import jenkins.model.Jenkins
import hudson.model.User
import hudson.security.Permission
import hudson.EnvVars
EnvVars envVars = build.getEnvironment(listener);
def userId= envVars .get('BUILD_USER_ID')
def nodeName= envVars .get('NODE_NAME')
def nodeOffpool= envVars .get('NODE_GOING_OFFPOOL')
allUsers = User.getAll()
println allUsers
println ""
// add userid as a label if doesnot exist
for (slave in hudson.model.Hudson.instance.slaves) {
if( slave.nodeName.equals(nodeOffpool)) {
def labelList = (slave.getLabelString()).split()
println labelList
// check for user access to machine
for(label in labelList) {
println (User.get(label))
println (User.get(label) in allUsers)
if (User.get(label) in allUsers) {
if (label == userId) {
println ("This Node has already been assigned to you ($userId)")
} else {
println ("This Node($nodeOffpool) has already been assigned to someone($label) else, you cannot use it now")
println ("Please ask the user $label to release the Node($nodeOffpool) for you($label) to run")
}
return
}
};
println ("before: " + slave.getLabelString())
// setting the slave with new label
String newLabel = slave.getLabelString() + " " + userId
slave.setLabelString(newLabel)
println ("after: " + slave.getLabelString())
}
}
When i run for the first time output looks fine
[user1, user2, SYSTEM, unknown]
[TESTLAB3, TESTLAB4]
TESTLAB3
false
TESTLAB4
false
before: TESTLAB3 TESTLAB4
after: TESTLAB3 TESTLAB4 user1
When i run the second time
[user1, user2, SYSTEM, unknown, TESTLAB3, TESTLAB4]
[TESTLAB3, TESTLAB4, user1]
TESTLAB3
true
This Node(node1) has already been assigned to someone(TESTLAB3) else, you cannot use it now
Please ask the user TESTLAB3 to release the Node(node1) for you(TESTLAB3) to run
Finished: SUCCESS
Is this issue with Jenkins API. I am using Jenkins 1.573
related questions:
How to identify admins on Jenkins using groovy scripts
jenkins change label as requested
Updated:
I found out the answer by trail and error.
User.get(label)
adds the label as user if it doesnt exist by default. To prevent this addition, we have to use
User.get(label, false)
You are adding the username to the slave's label. Printing User.getAll() just prints the list of users for your Jenkins, not the slave's labels, which you modified with the user name.
Here is a script you can use to test if your slave machines have the new labels added to them:
for (slave in jenkins.model.Jenkins.instance.slaves) {
print "Slave: " + slave.getNodeName() + "\n";
print "Label: " + slave.getLabelString() + "\n\n";
}
Also, you might want to change the script so that it only sets the new label for the Slave if it doesn't already contain the user, because you will be adding it multiple times every time the script is run the way it's written now.

How do you 'mention' users in this hubot cron script for Slack, so they can see it as notification if their notification settings are on?

I have this hubot-cron script set up with heroku for Slack. Works great with one exception. If you '#' someone (mention) in Slack, users will get a notification depending on their notification settings. The way this is written, if put in a users name in the command text, Slack doesn't register it as a user, simply as text.
For example:
hubot new job 2 * * * * * "#everyone testing"
When that is posted every two minutes, it may write #everyone, but Slack does not see it as actually '#everyone'.
What can be done with this code so where you can actually direct the job to #user?
# Description:
# register cron jobs to schedule messages on the current channel
#
# Commands:
# hubot new job "<crontab format>" <message> - Schedule a cron job to say something
# hubot new job <crontab format> "<message>" - Ditto
# hubot new job <crontab format> say <message> - Ditto
# hubot list jobs - List current cron jobs
# hubot remove job <id> - remove job
# hubot remove job with message <message> - remove with message
#
# Author:
# miyagawa
cronJob = require('cron').CronJob
JOBS = {}
createNewJob = (robot, pattern, user, message) ->
id = Math.floor(Math.random() * 1000000) while !id? || JOBS[id]
job = registerNewJob robot, id, pattern, user, message
robot.brain.data.cronjob[id] = job.serialize()
id
registerNewJobFromBrain = (robot, id, pattern, user, message, timezone) ->
# for jobs saved in v0.2.0..v0.2.2
user = user.user if "user" of user
registerNewJob(robot, id, pattern, user, message, timezone)
envelope = user: user, room: user.room
robot.send envelope, "Job #{id} registered from brain"
storeJobToBrain = (robot, id, job) ->
robot.brain.data.cronjob[id] = job.serialize()
envelope = user: job.user, room: job.user.room
robot.send envelope, "Job #{id} stored in brain asynchronously"
registerNewJob = (robot, id, pattern, user, message, timezone) ->
job = new Job(id, pattern, user, message, timezone)
job.start(robot)
JOBS[id] = job
unregisterJob = (robot, id)->
if JOBS[id]
JOBS[id].stop()
delete robot.brain.data.cronjob[id]
delete JOBS[id]
return yes
no
handleNewJob = (robot, msg, pattern, message) ->
try
id = createNewJob robot, pattern, msg.message.user, message
msg.send "Job #{id} created"
catch error
msg.send "Error caught parsing crontab pattern: #{error}. See http://crontab.org/ for the syntax"
updateJobTimezone = (robot, id, timezone) ->
if JOBS[id]
JOBS[id].stop()
JOBS[id].timezone = timezone
robot.brain.data.cronjob[id] = JOBS[id].serialize()
JOBS[id].start(robot)
return yes
no
syncJobs = (robot) ->
nonCachedJobs = difference(robot.brain.data.cronjob, JOBS)
for own id, job of nonCachedJobs
registerNewJobFromBrain robot, id, job...
nonStoredJobs = difference(JOBS, robot.brain.data.cronjob)
for own id, job of nonStoredJobs
storeJobToBrain robot, id, job
difference = (obj1, obj2) ->
diff = {}
for id, job of obj1
diff[id] = job if id !of obj2
return diff
module.exports = (robot) ->
robot.brain.data.cronjob or= {}
robot.brain.on 'loaded', =>
syncJobs robot
robot.respond /(?:new|add) job "(.*?)" (.*)$/i, (msg) ->
handleNewJob robot, msg, msg.match[1], msg.match[2]
robot.respond /(?:new|add) job (.*) "(.*?)" *$/i, (msg) ->
handleNewJob robot, msg, msg.match[1], msg.match[2]
robot.respond /(?:new|add) job (.*?) say (.*?) *$/i, (msg) ->
handleNewJob robot, msg, msg.match[1], msg.match[2]
robot.respond /(?:list|ls) jobs?/i, (msg) ->
text = ''
for id, job of JOBS
room = job.user.reply_to || job.user.room
if room == msg.message.user.reply_to or room == msg.message.user.room
text += "#{id}: #{job.pattern} ##{room} \"#{job.message}\"\n"
msg.send text if text.length > 0
robot.respond /(?:rm|remove|del|delete) job (\d+)/i, (msg) ->
if (id = msg.match[1]) and unregisterJob(robot, id)
msg.send "Job #{id} deleted"
else
msg.send "Job #{id} does not exist"
robot.respond /(?:rm|remove|del|delete) job with message (.+)/i, (msg) ->
message = msg.match[1]
for id, job of JOBS
room = job.user.reply_to || job.user.room
if (room == msg.message.user.reply_to or room == msg.message.user.room) and job.message == message and unregisterJob(robot, id)
msg.send "Job #{id} deleted"
robot.respond /(?:tz|timezone) job (\d+) (.*)/i, (msg) ->
if (id = msg.match[1]) and (timezone = msg.match[2]) and updateJobTimezone(robot, id, timezone)
msg.send "Job #{id} updated to use #{timezone}"
else
msg.send "Job #{id} does not exist"
class Job
constructor: (id, pattern, user, message, timezone) ->
#id = id
#pattern = pattern
# cloning user because adapter may touch it later
clonedUser = {}
clonedUser[k] = v for k,v of user
#user = clonedUser
#message = message
#timezone = timezone
start: (robot) ->
#cronjob = new cronJob(#pattern, =>
#sendMessage robot
, null, false, #timezone)
#cronjob.start()
stop: ->
#cronjob.stop()
serialize: ->
[#pattern, #user, #message, #timezone]
sendMessage: (robot) ->
envelope = user: #user, room: #user.room
robot.send envelope, #message
To mention a specific user, send one of the following formats
<#USER_ID>
<#USER_ID|user_name>
To use one of the special #mention commands like #everyone:
Currently defined commands are !channel, !group and !everyone (group is just a synonym for channel - both can be used in channels and groups). These indicate an #channel, #group or #everyone message, and should cause a notification to be displayed by the client.
So you'd want to send: <!everyone> to mention everyone.
So if I wanted to send a message mentioning stephen whose user id is U1234, I would send the following:
{
"text": "Hey <#U1234|stephen> pay attention!"
}
or if I wanted to mention #everyone, I would do:
{
"text": "Hey <!everyone> pay attention!"
}
Full docs on message formatting here: https://api.slack.com/docs/formatting
You should be using <!> for special words.
Some messages contain special words with extra behavior. For these we use the format <!foo>, where foo is a special command. - Slack
In your example, using <!everyone> to notify will work instead.

Do not delete a Jenkins build if it's marked as "Keep this build forever" - Groovy script to delete Jenkins builds

I have the following Groovy script which deletes all builds of a given Jenkins job except one build number that user provides (i.e. wants to retain).
/*** BEGIN META {
"name" : "Bulk Delete Builds except the given build number",
"comment" : "For a given job and a given build number, delete all build except the user provided one.",
"parameters" : [ 'jobName', 'buildNumber' ],
"core": "1.409",
"authors" : [
{ name : "Arun Sangal" }
]
} END META **/
// NOTE: Uncomment parameters below if not using Scriptler >= 2.0, or if you're just pasting the script in manually.
// ----- Logic in this script takes 5000 as the infinite number, decrease / increase this value from your own experience.
// The name of the job.
//def jobName = "some-job"
// The range of build numbers to delete.
//def buildNumber = "5"
def lastBuildNumber = buildNumber.toInteger() - 1;
def nextBuildNumber = buildNumber.toInteger() + 1;
import jenkins.model.*;
import hudson.model.Fingerprint.RangeSet;
def jij = jenkins.model.Jenkins.instance.getItem(jobName);
println("Keeping Job_Name: ${jobName} and build Number: ${buildNumber}");
println ""
def setBuildRange = "1-${lastBuildNumber}"
def range = RangeSet.fromString(setBuildRange, true);
jij.getBuilds(range).each { it.delete() }
println("Builds have been deleted - Range: " + setBuildRange)
setBuildRange = "${nextBuildNumber}-5000"
range = RangeSet.fromString(setBuildRange, true);
jij.getBuilds(range).each { it.delete() }
println("Builds have been deleted - Range: " + setBuildRange)
This works well for any Jenkins job. For ex: If your Jenkins job name is "TestJob" and you have 15 builds i.e. build# 1 to build 15 in Jenkins, and you want to delete all but retain build# 13, then this script will delete the builds (build# 1-12 and 14-15 - even if you mark any build as "Keep this build forever") and only keep build#13.
Now, what I want is:
what should I change in this script to not delete a build - if a build is marked in Jenkins as "Keep this build forever". I tried the script and it deleted that keep forever build too.
Lets say, if I'm using "Build name setter plugin" in Jenkins, which can give me build names as what name I want i.e. instead of getting just build as build#1 or #2, or #15, I will get build as build# 2.75.0.1, 2.75.0.2, 2.75.0.3, ..... , 2.75.0.15 (as I would have set the build name/description as use some variable which contains 2.75.0 (as a release version value) and suffixed it with the actual Jenkins job's build number i.e. the last 4th digit - ex: set the name as:
${ENV,var="somepropertyvariable"}.${BUILD_NUMBER}
In this case, I'll start getting Jenkins builds as 2.75.0.1 to 2.75.0.x (where x is the last build# of that release (2.75.0)). Similarly, when I'll change the property release version to next i.e. 2.75.1 or 2.76.0, then the same Jenkins job will start giving me builds as 2.75.1.0, 2.75.1.1, ...., 2.75.1.x or 2.76.0.1, 2.76.0.2, ...., 2.76.0.x and so on. During the release version change, let say, our build will start from 1 again (as I mentioned above for 2.75.1 and 2.76.0 release versions).
In this case, if my Jenkins job's build history (shows all builds for 2.75.0.x, 2.75.1.x and 2.76.0.x), then what change should I make in this script to include a 3rd parameter/argument. This 3rd argument will take release /version value i.e. either 2.75.0 or 2.75.1 or 2.76.0 and then this script should delete build numbers on that release only (and should NOT delete other release's builds).
If you want to test whether a build has been marked permanent, use
if (!build.isKeepLog()) {
// Build can be deleted
} else {
// Build is marked permanent
}
I think you should be able to use the getName() method on each build to check whether you should delete a given build. The API JavaDoc can be fairly obscure, so I often go on GitHub and look through the code for a Jenkins plugin that's doing something similar to what I need. The public Scriptler repository can be useful, too.
Final Answer: This includes deleting the build artifacts from Artifactory as well using Artifactor's REST API call. This script will delete Jenkins/Artifactory builds/artifacts of a given Release/Version (as sometimes over the time - a given Jenkins job can create multiple release / version builds for ex: 2.75.0.1, 2.75.0.2, 2.75.0.3,....,2.75.0.54, 2.76.0.1, 2.76.0.2, ..., 2.76.0.16, 2.76.1.1, 2.76.1.2, ...., 2.76.1.5). In this case, for every new release of that job, we start the build# from 1 fresh. If you have to delete the all builds except one / even all (change the script a little bit for your own needs) and don't change older/other release builds, then use the following script.
Scriptler Catalog link: http://scriptlerweb.appspot.com/script/show/103001
Enjoy!
/*** BEGIN META {
"name" : "Bulk Delete Builds except the given build number",
"comment" : "For a given job and a given build numnber, delete all builds of a given release version (M.m.interim) only and except the user provided one. Sometimes a Jenkins job use Build Name setter plugin and same job generates 2.75.0.1 and 2.76.0.43",
"parameters" : [ 'jobName', 'releaseVersion', 'buildNumber' ],
"core": "1.409",
"authors" : [
{ name : "Arun Sangal - Maddys Version" }
]
} END META **/
import groovy.json.*
import jenkins.model.*;
import hudson.model.Fingerprint.RangeSet;
import hudson.model.Job;
import hudson.model.Fingerprint;
//these should be passed in as arguments to the script
if(!artifactoryURL) throw new Exception("artifactoryURL not provided")
if(!artifactoryUser) throw new Exception("artifactoryUser not provided")
if(!artifactoryPassword) throw new Exception("artifactoryPassword not provided")
def authString = "${artifactoryUser}:${artifactoryPassword}".getBytes().encodeBase64().toString()
def artifactorySettings = [artifactoryURL: artifactoryURL, authString: authString]
if(!jobName) throw new Exception("jobName not provided")
if(!buildNumber) throw new Exception("buildNumber not provided")
def lastBuildNumber = buildNumber.toInteger() - 1;
def nextBuildNumber = buildNumber.toInteger() + 1;
def jij = jenkins.model.Jenkins.instance.getItem(jobName);
def promotedBuildRange = new Fingerprint.RangeSet()
promotedBuildRange.add(buildNumber.toInteger())
def promoteBuildsList = jij.getBuilds(promotedBuildRange)
assert promoteBuildsList.size() == 1
def promotedBuild = promoteBuildsList[0]
// The release / version of a Jenkins job - i.e. in case you use "Build name" setter plugin in Jenkins for getting builds like 2.75.0.1, 2.75.0.2, .. , 2.75.0.15 etc.
// and over the time, change the release/version value (2.75.0) to a newer value i.e. 2.75.1 or 2.76.0 and start builds of this new release/version from #1 onwards.
def releaseVersion = promotedBuild.getDisplayName().split("\\.")[0..2].join(".")
println ""
println("- Jenkins Job_Name: ${jobName} -- Version: ${releaseVersion} -- Keep Build Number: ${buildNumber}");
println ""
/** delete the indicated build and its artifacts from artifactory */
def deleteBuildFromArtifactory(String jobName, int deleteBuildNumber, Map<String, String> artifactorySettings){
println " ## Deleting >>>>>>>>>: - ${jobName}:${deleteBuildNumber} from artifactory"
def artifactSearchUri = "api/build/${jobName}?buildNumbers=${deleteBuildNumber}&artifacts=1"
def conn = "${artifactorySettings['artifactoryURL']}/${artifactSearchUri}".toURL().openConnection()
conn.setRequestProperty("Authorization", "Basic " + artifactorySettings['authString']);
conn.setRequestMethod("DELETE")
if( conn.responseCode != 200 ) {
println "Failed to delete the build artifacts from artifactory for ${jobName}/${deleteBuildNumber}: ${conn.responseCode} - ${conn.responseMessage}"
}
}
/** delete all builds in the indicated range that match the releaseVersion */
def deleteBuildsInRange(String buildRange, String releaseVersion, Job theJob, Map<String, String> artifactorySettings){
def range = RangeSet.fromString(buildRange, true);
theJob.getBuilds(range).each {
if ( it.getDisplayName().find(/${releaseVersion}.*/)) {
println " ## Deleting >>>>>>>>>: " + it.getDisplayName();
deleteBuildFromArtifactory(theJob.name, it.number, artifactorySettings)
it.delete();
}
}
}
//delete all the matching builds before the promoted build number
deleteBuildsInRange("1-${lastBuildNumber}", releaseVersion, jij, artifactorySettings)
//delete all the matching builds after the promoted build number
deleteBuildsInRange("${nextBuildNumber}-${jij.nextBuildNumber}", releaseVersion, jij, artifactorySettings)
println ""
println("- Builds have been successfully deleted for the above mentioned release: ${releaseVersion}")
println ""
OK - Solution for my question 2 is here: I'm still working on fixing question 1.
http://scriptlerweb.appspot.com/script/show/102001
bulkDeleteJenkinsBuildsExceptOne_OfAGivenRelease.groovy
/*** BEGIN META {
"name" : "Bulk Delete Builds except the given build number",
"comment" : "For a given job and a given build numnber, delete all builds of a given release version (M.m.interim) only and except the user provided one. Sometimes a Jenkins job use Build Name setter plugin and same job generates 2.75.0.1 and 2.76.0.43",
"parameters" : [ 'jobName', 'releaseVersion', 'buildNumber' ],
"core": "1.409",
"authors" : [
{ name : "Arun Sangal" }
]
} END META **/
// NOTE: Uncomment parameters below if not using Scriptler >= 2.0, or if you're just pasting the script in manually.
// ----- Logic in this script takes 5000 as the infinite number, decrease / increase this value from your own experience.
// The name of the job.
//def jobName = "some-job"
// The release / version of a Jenkins job - i.e. in case you use "Build name" setter plugin in Jenkins for getting builds like 2.75.0.1, 2.75.0.2, .. , 2.75.0.15 etc.
// and over the time, change the release/version value (2.75.0) to a newer value i.e. 2.75.1 or 2.76.0 and start builds of this new release/version from #1 onwards.
//def releaseVersion = "2.75.0"
// The range of build numbers to delete.
//def buildNumber = "5"
def lastBuildNumber = buildNumber.toInteger() - 1;
def nextBuildNumber = buildNumber.toInteger() + 1;
import jenkins.model.*;
import hudson.model.Fingerprint.RangeSet;
def jij = jenkins.model.Jenkins.instance.getItem(jobName);
//def build = jij.getLastBuild();
println ""
println("- Jenkins Job_Name: ${jobName} -- Version: ${releaseVersion} -- Keep Build Number: ${buildNumber}");
println ""
println " -- Range before given build number: ${buildNumber}"
println ""
def setBuildRange = "1-${lastBuildNumber}"
def range = RangeSet.fromString(setBuildRange, true);
jij.getBuilds(range).each {
if ( it.getDisplayName().find(/${releaseVersion}.*/)) {
println " ## Deleting >>>>>>>>>: " + it.getDisplayName();
// Trying to find - how to NOT delete a build in Jenkins if it's marked as "keep this build forever". If someone has an idea, please update this script with a newer version in GitHub.
//if ( !build.isKeepLog()) {
it.delete();
//} else {
// println "build -- can't be deleted as :" + build.getWhyKeepLog();
//}
}
}
println ""
println " -- Range after given build number: ${buildNumber}"
println ""
setBuildRange = "${nextBuildNumber}-5000"
range = RangeSet.fromString(setBuildRange, true);
jij.getBuilds(range).each {
if ( it.getDisplayName().find(/${releaseVersion}.*/)) {
println " ## Deleting >>>>>>>>>: " + it.getDisplayName();
it.delete();
}
}
println ""
println("- Builds have been successfully deleted for the above mentioned release: ${releaseVersion}")
println ""
One can also call this script via a Jenkins job (requires 3 parameters as mentioned in the scriptler script) -OR call it from browser as well: using the following link:
http ://YourJenkinsServerName:PORT/job/Some_Jenkins_Job_That_You_Will_Create/buildWithParameters?jobName=Test_AppSvc&releaseVersion=2.75.0&buildNumber=15

Resources