What's the best way to bulk update Jenkins projects? - jenkins

We have hundreds of Jenkins projects (mostly created from a few templates), often need to make the same change to all of them. e.g. today I need to add a post-build step to delete workspace at the end. Next I need to change the step to copy build result to a shared drive to Nexus repository.
What's the best way to apply such kind of bulk change to Jenkins projects?

You could use Configuration Slicing Plugin which is designed to do this.
It supports many configuration options.

The REST API is quite powerful. The following sequence worked for me:
In loop for all relevant projects (list of projects is available via e.g. /api/xml?tree=jobs[name]):
download config.xml via /job/{name}/config.xml
edit using your favorite scripted xml editor (mine was xmlstarlet)
upload new config xml via /job/{name}/config.xml
Some random notes:
do *BACKUP* before doing anything
I probably could post some bash script example if anyone is interested
Good luck!
EDIT> Example bash script:
#!/bin/bash
jenkinsUrlBase='http://user:token#jenkins'
callJenkins() { # funcPath
curl --silent --show-error -g "${jenkinsUrlBase}${1}"
}
postJenkinsFile() { # funcPath fileName
curl --silent --show-error -g -d "#${2}" "${jenkinsUrlBase}${1}"
}
callJenkins '/api/xml?tree=jobs[name]' | xmlstarlet sel -t -v '//hudson/job/name' | while read projectName ; do
echo "Processing ${projectName}..."
origFile="${projectName}_old.xml"
newFile="${projectName}_new.xml"
callJenkins "/job/${projectName}/config.xml" > "$origFile"
echo " - Updating artifactory url..."
cat "$origFile" \
| xmlstarlet ed -P -u '//maven2-moduleset/publishers/org.jfrog.hudson.ArtifactoryRedeployPublisher/details/artifactoryUrl' -v "http://newServer/artifactory" \
> "${newFile}"
if false ; then
echo " - Commiting new config file..."
postJenkinsFile "/job/${projectName}/config.xml" "$newFile"
else
echo " - Dry run: not commiting new config file"
fi
done

Groovy is by far the best way to bulk update jobs. You may have to do a little digging into the jenkins / plugin api to figure out what api calls to make, but the script console (http://yourJenkinsUrl/script) provides an easy way to play around with the code until you get it right.
To get you started, you can add / remove post-build steps by calling the getPublishersList() method on a job and then calling the add / remove methods.
def publishersList = Jenkins.instance.getJob("JobName").getPublishersList()
publishersList.removeAll { it.class == whatever.plugin.class }
publishersList.add(new PluginConstructor())
If you're not sure what publisher class you need to delete the workspace, I would suggest manually adding the desired configurations to one job, and then run getPublishersList() from the script console on that job. You will see the class you are working with in the list, and then you can go look at the api to see what is required to construct it.
You can then iterate through all your jobs and add the publisher doing something like this:
Jenkins.instance.getView("All Jobs").items.each { job ->
//Maybe some logic here to filter out specific jobs
job.getPublishersList().add(new PluginConstructor())
}
Alternatively, you can use the Jenkins CLI api or the REST api, but in order to update post-build actions, you will have to modify the project configuration xml file (which isn't trivial programmatically configure) and then overwrite the job configuration with the new configuration file.

You can edit the config.xml file with your favorite text tool (I use Python) and then reload the jenkins configuration.
In my setup the jobs are stored in ~/.jenkins/jobs/*/config.xml.
See: https://wiki.jenkins-ci.org/display/JENKINS/Administering+Jenkins
Here is a small example to update foo to bar:
</com.cwctravel.hudson.plugins.extended__choice__parameter.ExtendedChoiceParameterDefinition>
<hudson.model.StringParameterDefinition>
<name>additional_requirements</name>
<description>foo</description>
...
Script:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, unicode_literals, print_function
import sys
from lxml import etree
from collections import defaultdict
def change_parameter_description(config_xml_path, parameter_name, new_description):
tree=etree.parse(config_xml_path)
for tag in tree.findall('.//hudson.model.StringParameterDefinition'):
name_tag=tag.find('./name')
if not name_tag.text==parameter_name:
continue
description=tag.find('./description')
description.text=new_description
tree.write(config_xml_path)
for config_xml_path in sys.argv[1:]:
change_parameter_description(config_xml_path, 'additional_requirements', 'bar')
In this small example a regex would work, but if things span several lines, it is better to work with xml tools :-)

The other answers are great, but if you use pipelines, I'd suggest you to use Pipeline Shared Libraries.
We have all our jobs in a git repository. To develop a new feature we try it in a branch, since it is possible to point just one job to a specific branch. When we need to update them, just merge into master. The jobs are treated as code, with a proper release process.

Related

how to email html report of Jmeter execution via Jenkins the one which we get using -e -o in command line?

I want to integrate Jmeter with Jenkins for Functional and Performance testing. (Hope Jmeter is good for Functional testing and Performance both). Have created a Freestyle project with Necessary plugins, Performance, and Editable Email ones.
I am able to generate reports in Jenkins workspace using:
sh ./jmeter.sh -n -t jmx_path -l /workspace_path/result.csv -e -o /workspace_path/htmlReport
Also, able to attach index.html file in email. But the problem is it's not accessible as css and other related files are not there.
Please help me on this. Let me know if any other info is required from my end.
Thanks in advance.
Because sending index.html only is not sufficient, you need to send content and sbadmin2-1.0.7 folders as well so instead of sending index.html file you need to send the whole htmlReport folder
You can pack it like
sh 'zip -r /path/to/htmlReport.zip /workspace_path/htmlReport'
and then send the .zip file to the recipients of your choice
An easier option would be using Taurus tool a a wrapper for your JMeter test, it has online reports feature so you will only need to share the link

Trigger Jenkins Build on Perforce submit by keyword

I am wondering if anyone knows if it is possible to trigger a Jenkins build on a Perforce commit, only if it has a certain keyword in it?
My use case is that I am looking to find a way to trigger a Jenkins build that will build a Nuget package of the library I am working on, and place it in the correct Nuget server directory. I don't want to build a Nuget package each and every time I make a submit to Perforce, so I was wondering if it is possible to set up some kind of trigger to only run the Jenkins build if some keyword was included in the changelist description.
Does anyone know if this is possible, or any other possible ideas on how to implement this?
This works for me. I'm sure it could be more efficient, but it works. This looks for the string "buildit" in the changelist description.
First, create a change-submit trigger in Perforce, e.g.,
buildit change-submit //... "/usr/bin/buildthis %changelist%"
(wherever you place it should be writable, and create a file called "description" that should also be writable).
Below is the contents of the buildthis script:
#!/bin/sh
changelist="$1"
P4PORT=perforce:1666
P4CLIENT=myclient
P4USER=myuser
p4 -p $P4PORT -c $P4CLIENT -u $P4USER describe $changelist >/usr/bin/description
if grep -q buildit /usr/bin/description;
then
curl -X POST http://jenkinsserver:port/job/jobname/build?token=TOKEN
else
exit 0
fi

Jenkins - pass build name to code being built

I have my Jenkins job defined to use a timestamp for the build name. I'd also like to be able to use that build name in the code being built. For example, suppose my application prints a "startup" message during initialization. Ideally, I would be able to somehow inject the build name into this startup message.
Example:
Application XYZ, build 20160503-0420, is starting up...
I'm curious what sort of techniques folks have come up with to do something like this.
Thanks!
Well, at first insert build number directly into your code it`s very bad practice.
We create debian package by Jenkins. Debian packages contains control file, some kind of description of package, version, dependency, etc.
In our repo control file contains
Section: misc
Priority: optional
Package: #packagename
Version: 0.1.0+#build~#repo
As you see we keep fixed only major version of software
Then we create package we replace texts started by # with build variables
sed -i 's/#build/$(BUILD_NUMBER)/' $(FAKEROOT)/DEBIAN/control
sed -i 's/#repo/$(REPONAME)/' $(FAKEROOT)/DEBIAN/control
sed -i 's/#packagename/$(PROJECTNAME)/' $(FAKEROOT)/DEBIAN/control
And after that we publish, deploy, send email, doing all Jenkins magic.
I think you can build binary and distribute it
Of course if you want to add this changes into you repository you can use Git publisher, or another source management systems, but tell you again this is bad practice.
We use powershell plugin in Jenkins and this is how we write our job ...
Write-Host Build id $env:BUILD_NUMBER started at Get-Date
For more build parameters refer to below URL
https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project#Buildingasoftwareproject-JenkinsSetEnvironmentVariables

How to get surefire reports form Travis-CI build?

The question says it all really. How can I download or view the surefire-reports generated during a build on Travis?
You can just do
after_failure:
- cat target/surefire-reports/*.txt
Having not found a direct way to access the surefire-report files I came up with the this workaround:
In .travis.yml I added an after_failure hook:
after_failure: print_surefire_reports.sh
In the hook print_surefire_reports.sh I put:
#!/usr/bin/env sh
echo "Current directory is $(pwd)"
echo "\n=== SUREFIRE REPORTS ===\n"
for F in target/surefire-reports/*.txt
do
echo $F
cat $F
echo
done
I am using python html2text in travis after script phase.
My travis script looks like:
after_script:
- python html2text.py target/site/surefire-report.html
surefire-report.html is generated by surefire-report-plugin
See example output here: https://travis-ci.org/rmpestano/dbunit-rules/builds/160170324#L3541
Based on this bug report and this question, there is not a clean way to do this. There are, however, a couple unsupported methods of getting the reports listed in the bug report.
Those may provide options for you to look into, but the Travis CI maintainers have not provided or supported an explicit way to handle this yet. Note that those bug reports/questions are well over a year old too.
The prevailing suggestion in those threads seems to be to have Travis recommit the build artifacts back to the user's repository. This, however, requires authentication, which you probably shouldn't store in your .travis.yml file

hudson CI: how to delete all jobs?

I have about 100 jobs on my hudson CI, possible to mass delete them ?
The easiest way, IMHO, would be to use script. Go to http://your.hudson.url/script/
Delete jobs by running:
for(j in hudson.model.Hudson.theInstance.getProjects()) {
j.delete();
}
And this way gives you an option to easily use condition to filter out jobs to delete.
FOR JENKINS
Current versions (2.x):
for(j in jenkins.model.Jenkins.theInstance.getAllItems()) {
j.delete()
}
Older versions:
for(j in jenkins.model.Jenkins.getInstance().getProjects()) {
j.delete();
}
Just delete the job directories:
cd $HUDSON_HOME/jobs
rm -rf <JOB_NAME>
See: Administering Hudson
You can programmatically use the XML api (or use the JSON flavor if you prefer that):
http://your.hudson.url/api/xml?xpath=//job/name&wrapper=jobs
Returns:
<jobs>
<name>firstJob</name>
<name>secondJob</name>
<!-- etc -->
</jobs>
Now iterate over the job names and do a post request to
http://your.hudson.url/job/your.job.name/doDelete
(You can do this with any programming language you like that supports XML and HTTP)
I had similar manageability problems with a Hudson instance that was running 500+ build jobs - it was impractical to manually maintain that many jobs using the gui. However, you can provision jobs in Hudson remotely and programatically by using the CLI - which is supplied as a jar file [http://wiki.hudson-ci.org/display/HUDSON/Hudson+CLI].
The command to delete a job would be something like:
**java -jar hudson-cli.jar -s http://host:port/ delete-job jobname**
And the rest of the commands you will need are here:
**java -jar hudson-cli.jar -s http://host:port/** help
I wrapped the cli in python and created an XML file from which to hold the build configuration - then I could use this to manipulate my running instances of Hudson. This also provided the ability to 'reset' the CI instance back to a known configuration - handy if you suspect build failures were caused by manual changes in the UI or if you are using a different CI server for each environment you deploy to (ie dev, test, prod) and need to provision a new one.
This has also got me out of a few binds when badly written plugins have mangled Hudson's own XML and I've needed to rebuild my instances. Hudson is also I/O bound and for really loaded instances it is often faster to boot Hudson from scratch and populate it's configuration this way.

Resources