首页 文章

在推广/推广步骤中删除jenkins构建

提问于
浏览
0

Jenkins :版本1.525
Jenkins Server URLhttp://my.jenkins.server.com:9040
Linux 红帽5.3

Artifactory :免费版
Artifactory Server URLhttp://my.artifactory.server:8081/Artifactory

我已成功构建Jenkins并将工件上传到我的Artifactory服务器下的相应存储库 .

发生构建时,工件(ProjectA-1.0.0.25.tar.gz)将转到libs-snapshot-local存储库下的Artifactory . 这里1.0.0表示应用程序/ jenkins作业的给定版本的主要版本,次要版本和临时版本:在这种情况下为"ProjectA" . 可以说25是内部版本号

当ProjectA构建在开发中变得稳定时,我们将该应用程序版本的给定构建推广到INT或任何其他更高的环境(QA / PrePROD等) .

在此促销过程中,我们只选择要推广和使用Jenkins Promoted Build Plugin的构建,我们能够成功完成 .

Now, what we need is:

  • 在推广过程中,我想调用一个Groovy脚本,该脚本将在"ProjectA-1.0.0.25.tar.gz"成功提升为INT后删除ProjectA的1.0.0版本中Jenkins和Artifactory(libs-snapshot-local)的所有Jenkins构建 . 促销部分现在正常运作;我只需要一个Groovy脚本,它将删除Jenkins中的Jenkins构建(1.0.0.1到1.0.0.24和> = 1.0.0.25)及其与Artifactory存储库(libs-snapshot-local)相关联的工件 .

我们公司的想法是,一旦发布版本's build is promoted for an application, all other builds / artifacts we have (in Jenkins/Artifactory) - we want to delete forever using a Groovy script. Someone will ask, what if I want to promote a different build#; well in our case, we dont want that. Simple rule is, if someone promotes ProjectA-1.0.0.25.tar.gz, then, delete ProjectA'在Jenkins和Artifactory中构建/工件,其中构建/工件不是1.0.0.25并继续新版本1.1.0

具有以下功能的脚本会很棒 .
1.使用属性文件(jenkins.properties / artifactory.properties) - 其中包含一些关于主机名/用户名/密码等的变量(如果有的话) .
2.使用REST API对给定的应用程序/作业执行删除并给予发布(对于前1.0.0)
3.可以用于Jenkins / Artifactory删除 - 如果在命令提示符下,我说使用这个(Jenkins)属性文件 - 或那个(Artifatory) - 因为在这两种情况下,应用程序及其发布值都是相同的 .
4.我们知道,为了促进INT的构建(使用Jenkins推广插件),我们将始终只在libs-snapshot-release从Jenkins服务器和Artifactory服务器中删除 .
现在,如果某人升级到QA(稍后),那么神器库将是(libs-stage-local)

换句话说,我们应该调用Groovy脚本,传递一些变量/值(REST)并告诉要删除哪个应用程序/作业以及它的构建版本 . 然后,它将删除除用户将通过的构建之外的所有构建(即1.0.0.25)

我是Groovy /使用REST API的新手,为Jenkins / Artifactory做这个“删除”工作 . 如果有人已经有任何样本脚本执行此类活动,并且您可以共享,我将根据我的设置调整它,看看我是否可以在升级步骤中看到上述行为 . 我有一些时间来处理这个脚本的工作版本,会很感激一些脚本代码执行相同的任务(而不是很棒的人告诉我通过大文档/链接,我知道这将使我成为一个更好的编码器在Groovy但它会延迟这篇文章的整个目的) .

Thanks a lot.

5 回答

  • 0

    找到了一种方法(此时不使用REST API调用,但很快我会更新,或者你可以提供帮助) .

    解决方案1 - 删除除一个构建(我们选择用于促销)之外的Jenkins作业的所有构建,因此,在促销期间,我们将在Jenkins的BUILD部分下调用“scriptler”脚本,脚本具有以下代码或创建一个单独的作业并通过传递2个参数(jobName和buildNumber - Jenkins作业中的字符串参数)来调用此脚本 .

    -bash-3.2 $ cat bulkDeleteBuildsExceptOne.groovy

    /*** BEGIN META {
      "name" : "Bulk Delete Builds except the given build number",
      "comment" : "For a given job and a given build numnber, delete all build except the user provided one.",
      "parameters" : [ 'jobName', 'buildNumber' ],
      "core": "1.410",
      "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}"
    
    //println setBuildRange
    
    def range = RangeSet.fromString(setBuildRange, true);
    
    jij.getBuilds(range).each { it.delete() }
    
    println("Builds have been deleted - Range: " + setBuildRange)
    
    
    setBuildRange = "${nextBuildNumber}-5000"
    
    //println setBuildRange
    
    range = RangeSet.fromString(setBuildRange, true);
    
    jij.getBuilds(range).each { it.delete() }
    
    println("Builds have been deleted - Range: " + setBuildRange)
    

    https://github.com/gigaaks/jenkins-scripts/blob/master/scriptler/bulkDeleteBuildsExceptOne.groovy -OR http://scriptlerweb.appspot.com/script/show/101001(Scriptler Web站点) - 这可以在远程目录脚本部分下的Jenkins Scriptler插件中看到 .

    如果GITHUB人员提供一个简单的按钮/链接到PUSH我对主jenkinsci分支/存储库的更改会更容易一些 .

    Though I'm still looking for 2 things:

    • 如何在Groovy中对以下脚本进行参数化 . 使用CliBuilder,我发现class not found错误 .

    • 如何使用Jenkins REST API调用执行此操作 . 稍后,我将使用Artifactory REST API调用执行相同的操作 .

  • -1

    好 . 有一点调整 . 发现如果Jenkins作业从单个作业生成多个发布/版本构建/工件(即,如果它使用Build Name Setter插件)并使用Major.minor.interim(2.75.0 for ex . )因为它的发布并为此版本生成了1-150版本,之后一旦该版本发布到INT / QA env,同样的工作是为下一版本创建1-N编号的版本(即2.75) .1或2.76.0或者等等 . )然后以下脚本就可以了 .

    看到这个链接:Do not delete a Jenkins build if it's marked as "Keep this build forever" - Groovy script to delete Jenkins builds

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

    enter image description here

  • 0

    使用REST API调用上面的脚本编写器脚本或者Jenkins作业就像:想知道我在哪里传递POST操作 .

    主线是:
    def artifactSearchUri = "api/build/$/$"
    ...我们需要调整如下:

    = "api/build/Some_Jenkins_Job_That_You_Will_Create/buildWithParameters?jobName=Test_AppSvc&releaseVersion=2.75.0&buildNumber=15"

    import groovy.json.*
    def artifactoryURL= properties["jenkins.ARTIFACTORY_URL"]
    def artifactoryUser = properties["artifactoryUser"]
    def artifactoryPassword = properties["artifactoryPassword"]
    def authString = "${artifactoryUser}:${artifactoryPassword}".getBytes().encodeBase64().toString()
    def jobName = properties["jobName"]
    def buildNumber = properties["buildNumber"]
    def artifactSearchUri = "api/build/${jobName}/${buildNumber}"
    def conn = "${artifactoryURL}/${artifactSearchUri}".toURL().openConnection()
    conn.setRequestProperty("Authorization", "Basic " + authString);
    println "Searching artifactory with: ${artifactSearchUri}"
    def searchResults
    if( conn.responseCode == 200 ) {
    searchResults = new JsonSlurper().parseText(conn.content.text)
    } else {
    throw new Exception ("Failed to find the build info for ${jobName}/${buildNumber}: ${conn.responseCode} - ${conn.responseMessage}")
    }
    

    并且为了删除Artifactory构建,我们必须使用以下groovy脚本来支持上述逻辑,我仍然试图让它工作 . 我知道我很亲密 .

    BLOG: http://browse.feedreader.com/c/Gridshore/11546011
    Script: https://github.com/jettro/small-scripts/blob/master/groovy/artifactory/Artifactory.groovy

    package artifactory
    
    import groovy.text.SimpleTemplateEngine
    import groovyx.net.http.RESTClient
    import net.sf.json.JSON
    
    /**
     * This groovy class is meant to be used to clean up your Atifactory server or get more information about it's
     * contents. The api of artifactory is documented very well at the following location
     * {@see http://wiki.jfrog.org/confluence/display/RTF/Artifactory%27s+REST+API}
     *
     * At the moment there is one major use of this class, cleaning your repository.
     *
     * Reading data about the repositories is done against /api/repository, if you want to remove items you need to use
     * '/api/storage'
     *
     * Artifactory returns a strange Content Type in the response. We want to use a generic JSON library. Therefore we need
     * to map the incoming type to the standard application/json. An example of the mapping is below, all the other
     * mappings can be found in the obtainServerConnection method.
     * 'application/vnd.org.jfrog.artifactory.storage.FolderInfo+json' => server.parser.'application/json'
     *
     * The class makes use of a config object. The config object is a map with a minimum of the following fields:
     * def config = [
     *       server: 'http://localhost:8080',
     *       repository: 'libs-release-local',
     *       versionsToRemove: ['/3.2.0-build-'],
     *       dryRun: true]
     *
     * The versionsToRemove is an array of strings that are the strart of builds that should be removed. To give an idea of
     * the build numbers we use: 3.2.0-build-1 or 2011.10-build-1. The -build- is important for the solution. This is how
     * we identify an artifact instead of a group folder.
     *
     * The final option to notice is the dryRun option. This way you can get an overview of what will be deleted. If set
     * to false, it will delete the selected artifacts.
     *
     * Usage example
     * -------------
     * def config = [
     *        server: 'http://localhost:8080',
     *        repository: 'libs-release-local',
     *        versionsToRemove: ['/3.2.0-build-'],
     *        dryRun: false]
     *
     * def artifactory = new Artifactory(config)
     *
     * def numberRemoved = artifactory.cleanArtifactsRecursive('nl/gridshore/toberemoved')
     *
     * if (config.dryRun) {*    println "$numberRemoved folders would have been removed."
     *} else {*    println "$numberRemoved folders were removed."
     *}* @author Jettro Coenradie
     */
    private class Artifactory {
        def engine = new SimpleTemplateEngine()
        def config
    
        def Artifactory(config) {
            this.config = config
        }
    
        /**
         * Print information about all the available repositories in the configured Artifactory
         */
        def printRepositories() {
            def server = obtainServerConnection()
            def resp = server.get(path: '/artifactory/api/repositories')
            if (resp.status != 200) {
                println "ERROR: problem with the call: " + resp.status
                System.exit(-1)
            }
            JSON json = resp.data
            json.each {
                println "key :" + it.key
                println "type : " + it.type
                println "descritpion : " + it.description
                println "url : " + it.url
                println ""
            }
        }
    
        /**
         * Return information about the provided path for the configured  artifactory and server.
         *
         * @param path String representing the path to obtain information for
         *
         * @return JSON object containing information about the specified folder
         */
        def JSON folderInfo(path) {
            def binding = [repository: config.repository, path: path]
            def template = engine.createTemplate('''/artifactory/api/storage/$repository/$path''').make(binding)
            def query = template.toString()
    
            def server = obtainServerConnection()
    
            def resp = server.get(path: query)
            if (resp.status != 200) {
                println "ERROR: problem obtaining folder info: " + resp.status
                println query
                System.exit(-1)
            }
            return resp.data
        }
    
        /**
         * Recursively removes all folders containing builds that start with the configured paths.
         *
         * @param path String containing the folder to check and use the childs to recursively check as well.
         * @return Number with the amount of folders that were removed.
         */
        def cleanArtifactsRecursive(path) {
            def deleteCounter = 0
            JSON json = folderInfo(path)
            json.children.each {child ->
                if (child.folder) {
                    if (isArtifactFolder(child)) {
                        config.versionsToRemove.each {toRemove ->
                            if (child.uri.startsWith(toRemove)) {
                                removeItem(path, child)
                                deleteCounter++
                            }
                        }
                    } else {
                        if (!child.uri.contains("ro-scripts")) {
                            deleteCounter += cleanArtifactsRecursive(path + child.uri)
                        }
                    }
                }
            }
            return deleteCounter
        }
    
        private RESTClient obtainServerConnection() {
            def server = new RESTClient(config.server)
            server.parser.'application/vnd.org.jfrog.artifactory.storage.FolderInfo+json' = server.parser.'application/json'
            server.parser.'application/vnd.org.jfrog.artifactory.repositories.RepositoryDetailsList+json' = server.parser.'application/json'
    
            return server
        }
    
        private def isArtifactFolder(child) {
            child.uri.contains("-build-")
        }
    
        private def removeItem(path, child) {
            println "folder: " + path + child.uri + " DELETE"
            def binding = [repository: config.repository, path: path + child.uri]
            def template = engine.createTemplate('''/artifactory/$repository/$path''').make(binding)
            def query = template.toString()
            if (!config.dryRun) {
                def server = new RESTClient(config.server)
                server.delete(path: query)
            }
        }
    }
    
  • 0

    FINAL Answer :这包括使用Artifactor删除Artifactory中的构建工件'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更改旧版/其他版本构建,然后使用以下脚本 .

    Scriptler Catalog linkhttp://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 ""
    
  • 0

    首先,但它没有完成 . 你为什么要删除:

    • 一些不会伤害你的额外Jenkins构建 .

    • 从工件存储库(名为Artifactory !!)中删除工件

    既然这样说,我知道你可能还有充分的理由这样做(这很有趣) . 这是我可以提议的另一种方法:

    • Jenkins :我假设您正在使用Maven . 在这种情况下,您使用M2 release plugin来创建"Release Builds" . 现在这些构建将在它们旁边有一个特殊的手提箱图标,并且将是"keep this build for ever" . 您可以在Jenkins中花多少天来保存工件,保留多少构建等,并制定自己的策略以满足您的需求 .

    • Artifactory :我使用Nexus,因此实现可能会有所不同 . 但您可以对其进行设置,以便每次都覆盖快照构建 . 因此,您始终拥有 n 版本的发布版本和1个快照 . 第二项政策是"delete snapshot when released" . 这确保了相同编号的快照和发布在repo中不共存 . 现在这正是应该如何,并且没有理由从诸如Artifactory的存储库中删除"released"工件 . 这是发布的全部内容 .

相关问题