Skip to content

Instantly share code, notes, and snippets.

@taherbs
Last active March 25, 2024 06:01
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save taherbs/6d03b4d56ac4f1e7a119e64cf5d17f4c to your computer and use it in GitHub Desktop.
Save taherbs/6d03b4d56ac4f1e7a119e64cf5d17f4c to your computer and use it in GitHub Desktop.
jenkins safe auto update plugins
//List all active plugins and save them into a file
def list_jenkins_plugins(directory, fileName) {
File pluginsListFile = new File("$directory/$fileName")
jenkins.model.Jenkins.instance.pluginManager.activePlugins.findAll { plugin ->
pluginsListFile.append("${plugin.getDisplayName()} (${plugin.getShortName()}): ${plugin.getVersion()}" + System.getProperty("line.separator"))
}
}
//Perform jenkins plugin update in a safe manner
def jenkins_safe_plugins_update() {
//Refresh plugins updates list
jenkins.model.Jenkins.getInstanceOrNull().getUpdateCenter().getSites().each { site ->
site.updateDirectlyNow(hudson.model.DownloadService.signatureCheck)
}
hudson.model.DownloadService.Downloadable.all().each { downloadable ->
downloadable.updateNow();
}
//Get the list of plugins
def pluginsToUpdate = []
def pluginsToReviewManually = []
def pluginsDeprecated = []
jenkins.model.Jenkins.instance.pluginManager.activePlugins.findAll { plugin ->
if (!(plugin.getDeprecations().isEmpty())) {
pluginsDeprecated.add(plugin.getDisplayName())
} else if (plugin.hasUpdate()) {
if (plugin.getActiveWarnings().isEmpty()) {
pluginsToUpdate.add(plugin.getShortName())
}
else {
pluginsToReviewManually.add(plugin.getDisplayName())
}
}
}
println "Plugins to upgrade automatically: ${pluginsToUpdate}"
println "Plugins to review and update manually: ${pluginsToReviewManually}"
println "Plugins depricated: ${pluginsDeprecated}"
long count = 0
jenkins.model.Jenkins.instance.pluginManager.install(pluginsToUpdate, false).each { f ->
f.get()
println "${++count}/${pluginsToUpdate.size()}.."
}
if(pluginsToUpdate.size() != 0 && count == pluginsToUpdate.size()) {
jenkins.model.Jenkins.instance.safeRestart()
}
return [ pluginsToReviewManually, pluginsDeprecated ]
}
return this
def DATETIME = new Date().format('yyyy_MM_dd_HH_mm_ss', TimeZone.getTimeZone('Canada/Eastern'))
def pluginsToReviewManually = []
def pluginsDeprecated = []
pipeline {
agent { label 'master' }
options {
//Build options
disableConcurrentBuilds()
buildDiscarder(
logRotator (
artifactDaysToKeepStr: '10',
artifactNumToKeepStr: '1',
daysToKeepStr: '30',
numToKeepStr: '30'
)
)
}
triggers { cron('TZ=Canada/Eastern\n0 0 * * 7') }
environment {
GOOGLE_CHAT_TOKEN = 'XXX-XXX-XXX-XXX'
}
stages {
stage('Update_Plugins') {
steps {
script {
def safePluginUpdateModule = load("${WORKSPACE}/jenkins_auto_update_plugins/jenkins-plugins-uptodate.groovy")
safePluginUpdateModule.list_jenkins_plugins("${WORKSPACE}/jenkins_auto_update_plugins", "plugins_list_BEFORE-UPDATE_${DATETIME}.txt")
(pluginsToReviewManually, pluginsDeprecated) = safePluginUpdateModule.jenkins_safe_plugins_update()
safePluginUpdateModule.list_jenkins_plugins("${WORKSPACE}/jenkins_auto_update_plugins", "plugins_list_AFTER-UPDATE_${DATETIME}.txt")
}
}
}
}
post {
always {
script {
archiveArtifacts "jenkins_auto_update_plugins/plugins_list_*_${DATETIME}.txt"
if (!(pluginsToReviewManually.isEmpty())) {
hangoutsNotify message: "IMPORTANT!!! The following plugins need to get reviewed and updated manually: ${pluginsToReviewManually}",token: "${env.GOOGLE_CHAT_TOKEN}",threadByJob: true
} else if (!(pluginsDeprecated.isEmpty())) {
hangoutsNotify message: "IMPORTANT!!! The following plugins are deprecated and need to be deleted: ${pluginsDeprecated}",token: "${env.GOOGLE_CHAT_TOKEN}",threadByJob: true
}
}
}
failure {
hangoutsNotify message: "${JOB_BASE_NAME} faild!",token: "${env.GOOGLE_CHAT_TOKEN}",threadByJob: true
}
}
}
@robertwnorris
Copy link

Hi
Unless I'm missing something you seem to have posted Jenkinsfile twice instead of jenkins-plugins-uptodate.groovy and Jenkinsfile.

I'm interested in taking a look at your improved jenkins-plugins-uptodate.groovy if you still have it.

Also, you have blanked the google chat token on the second file but left it visible in the first.

Cheers,
Rob

@taherbs
Copy link
Author

taherbs commented Jul 23, 2021

my mistake! fixing that :D thank you for pointing that to me!

@taherbs
Copy link
Author

taherbs commented Jul 23, 2021

Fixed

@robertwnorris
Copy link

robertwnorris commented Jul 23, 2021 via email

@radnov
Copy link

radnov commented Sep 28, 2021

Hey @taherbs,

Have you found a way to also create backups of the installed plugins (similar to doing an update through the Plugin Manager UI), when calling the Update Center and Plugin Manager with Groovy script?

Previous version of plugins are kept in the $JENKINS_HOME/plugins dir as .bak files, but I've been looking at the Jenkins Core source code and can't find the right way to do it through groovy script. This is supposed to be the replace method that is taking care of the backup after the new version has been downloaded and it's what the install() method is using within the same class, so not sure what is missing.

Any ideas? 🤔

@raphperrin
Copy link

Hey @taherbs
By any chance did you implement a compatibility check ? In my case it updated all plugins even the one with warning and "not compatible".

@taherbs
Copy link
Author

taherbs commented Dec 7, 2021

@radnov did not look into that use case for the moment.
@raphperrin I have those 2 checks:

plugin.getActiveWarnings() -> to check if we have warning
plugin.getDeprecations() -> check if the plugin got deprecated

if we hit one of those conditions a notificatation get sent and a manual review is required, I did not find a way to fully automate that part

@vvursT
Copy link

vvursT commented Mar 8, 2022

Thank you for providing this useful script. We use the plugin Script-Security which is complaining all the insecure methods that are used in this script. Do you know any other way to approve script security instead of whitelisting all method calls? Thank you in advice.

@romancin
Copy link

romancin commented Nov 4, 2022

Thank you very much for this!!

Here you have the list of signatures used by this script you need to approve:

field hudson.PluginManager activePlugins
method hudson.PluginManager install java.util.Collection boolean
method hudson.PluginWrapper getActiveWarnings
method hudson.PluginWrapper getDeprecations
method hudson.PluginWrapper getShortName
method hudson.PluginWrapper getVersion
method hudson.PluginWrapper hasUpdate
method hudson.model.DownloadService$Downloadable updateNow
method hudson.model.UpdateCenter getSites
method hudson.model.UpdateSite updateDirectlyNow boolean
method java.util.concurrent.Future get
method jenkins.model.Jenkins getPluginManager
method jenkins.model.Jenkins getUpdateCenter
method jenkins.model.Jenkins safeRestart
new java.io.File java.lang.String
staticField hudson.model.DownloadService signatureCheck
staticMethod hudson.model.DownloadService$Downloadable all
staticMethod java.lang.System getProperty java.lang.String
staticMethod jenkins.model.Jenkins getInstance
staticMethod jenkins.model.Jenkins getInstanceOrNull
staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods append java.io.File java.lang.Object

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment