【问题标题】:Do not delete a Jenkins build if it's marked as "Keep this build forever" - Groovy script to delete Jenkins builds如果 Jenkins 构建被标记为“永远保留此构建”,请不要删除它 - 删除 Jenkins 构建的 Groovy 脚本
【发布时间】:2013-09-28 19:25:07
【问题描述】:

我有以下 Groovy 脚本,它删除给定 Jenkins 作业的所有构建,除了用户提供的一个构建号(即想要保留)。

/*** 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)

这适用于任何 Jenkins 工作。例如:如果您的 Jenkins 作业名称是“TestJob”并且您有 15 个构建,即构建#1 在 Jenkins 中构建 15,并且您想要删除所有构建但保留构建#13,那么此脚本将删除构建(构建#1 -12 和 14-15 - 即使您将任何构建标记为“永久保留此构建”)并且只保留构建#13。


现在,我想要的是:

  1. 如果构建在 Jenkins 中被标记为“永久保留此构建”,我应该在此脚本中进行哪些更改才能不删除构建。我尝试了这个脚本,它也删除了 keep forever build。

  2. 比方说,如果我在 Jenkins 中使用“构建名称设置器插件”,它可以给我构建名称作为我想要的名称,而不是仅仅构建为构建 #1 或 #2 或 #15,我将构建为 build# 2.75.0.1, 2.75.0.2, 2.75.0.3, ..... , 2.75.0.15 (因为我会将构建名称/描述设置为使用一些包含 2.75.0 的变量(作为发布版本值)并以实际 Jenkins 作业的内部版本号(即最后 4 位)作为后缀 - 例如:将名称设置为:

    ${ENV,var="somepropertyvariable"}.${BUILD_NUMBER}
    

    在这种情况下,我将开始将 Jenkins 构建为 2.75.0.1 到 2.75.0.x(其中 x 是该版本 (2.75.0) 的最后一个构建#)。同样,当我将属性发布版本更改为下一个版本,即 2.75.1 或 2.76.0 时,相同的 Jenkins 工作将开始为我提供 2.75.1.0、2.75.1.1、......、2.75.1 的构建版本。 x 或 2.76.0.1、2.76.0.2、.....、2.76.0.x 等等。在发布版本更改期间,假设我们的构建将再次从 1 开始(正如我在上面提到的 2.75.1 和 2.76.0 发布版本)。

    在这种情况下,如果我的 Jenkins 作业的构建历史记录(显示 2.75.0.x、2.75.1.x 和 2.76.0.x 的所有构建),那么我应该在此脚本中进行哪些更改以包含第三个参数/参数。这第三个参数将采用版本/版本值,即 2.75.0 或 2.75.1 或 2.76.0,然后此脚本应仅删除该版本上的构建号(并且不应删除其他版本的构建)。

【问题讨论】:

    标签: groovy jenkins build forever


    【解决方案1】:

    最终答案:这包括从 Artifactory 中删除构建工件以及使用 Artifactor 的 REST API 调用。此脚本将删除给定版本/版本的 Jenkins/Artifactory 构建/工件(有时随着时间的推移 - 给定的 Jenkins 作业可以创建多个版本/版本构建,例如: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)。在这种情况下,对于该作业的每个新版本,我们都会从 1 重新开始 build#。如果您必须删除除一个/甚至全部之外的所有构建(根据您自己的需要稍微更改脚本)并且不要更改较旧/其他版本的构建,请使用以下脚本。

    Scriptler 目录链接http://scriptlerweb.appspot.com/script/show/103001

    享受吧!

    /*** 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 ""
    

    【讨论】:

      【解决方案2】:

      好的 - 我的问题 2 的解决方案在这里:我仍在努力解决问题 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 ""
      

      也可以通过 Jenkins 作业调用此脚本(需要在 scriptler 脚本中提到的 3 个参数)-或者也可以从浏览器调用它:使用以下链接:

      http://YourJenkinsServerName:PORT/job/Some_Jenkins_Job_That_You_Will_Create/buildWithParameters?jobName=Test_AppSvc&releaseVersion=2.75.0&buildNumber=15

      【讨论】:

      • 这个脚本可以使用 Jenkins "Scriptler" 插件运行良好。输出见附图。如果您想从 Jenkins 作业运行此脚本,则在 Build 部分下,调用 Scriptler 脚本,提供位置/文件名,勾选 jenkins 作业将使用此 scriptler 脚本的参数(如上所示),就是这样。这个 jenkins 作业需要 3 个参数字符串格式 - jobName、releaseVersion 和 buildNumber。
      【解决方案3】:

      如果要测试构建是否已标记为永久,请使用

      if (!build.isKeepLog()) {
          // Build can be deleted
      } else {
          // Build is marked permanent
      }
      

      我认为您应该能够在每个构建上使用getName() method 来检查您是否应该删除给定的构建。 API JavaDoc 可能相当晦涩难懂,所以我经常继续 GitHub 并查看 Jenkins 插件的代码,该插件正在做与我需要的类似的事情。 public Scriptler repository 也很有用。

      【讨论】:

      • 我从哪里得到这个“构建”(在 build.isKeepLo​​g 中),谢谢
      • 构建 = jij.getLastBuild(); if ( !build.isKeepLo​​g()) 在这两种情况下都返回 false - 如果构建被标记为永久保留 - 或者不。
      • 这听起来不对;我有脚本使用 isKeepLo​​g() 来控制构建是否是永久性的并且它们工作正常。你能把你的整个脚本贴在某个地方,让我看看吗?
      • 当然。我确实尝试包含它,将构建设置永久保留,当我打印 build.isKeepLo​​g() 时它返回 false。请参阅我的最终答案脚本,该脚本确实从 Jenkins 和 Artifactory 中删除构建 Jenkins 作业的给定版本/版本的工件。如果您可以制作该脚本 - 不要删除“永远保留”的构建,那就太棒了。
      • 您必须在以下行之前包含 if (!build.isKeepLo​​g()) ... 的逻辑:it.delete();在最终脚本中。
      猜你喜欢
      • 1970-01-01
      • 2011-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-08
      • 1970-01-01
      • 1970-01-01
      • 2022-01-27
      相关资源
      最近更新 更多