当前位置: 首页 > 知识库问答 >
问题:

使用REST API删除Artifactory构建工件

郭修平
2023-03-14

我有以下构建文物在艺术工厂服务器。

http://artifactory.company.com:8081/artifactory/libs-snapshot-local/com/mycompany/projectA/service_y/2.75.0.1/service_y-2.75.0.1.jar

http://artifactory.company.com:8081/artifactory/libs-snapshot-local/com/mycompany/projectA/service_y/2.75.0.2/service_y-2.75.0.2.jar

http://artifactory.company.com:8081/artifactory/libs-快照local/com/mycompany/projectA/service_y/2.75.0.3/service_y-2.75.0.3。罐子

http://artifactory.company.com:8081/artifactory/libs-快照local/com/mycompany/projectA/service_y/2.75.0.4/service_y-2.75.0.4。罐子

问题:

>

  • 我想要一个漂亮的脚本来删除上面的工件,除了2.75.0.3. jar(脚本应该使用艺术工厂REST API)。有人有一个样例脚本来做这件事吗?或者至少在这种情况下删除所有的. jars?

    我如何在groovy脚本中使用以下用法。
    用于ex:在groovy中使用以下行

    DELETE /api/build/{buildName}[?buildNumbers=n1[,n2]][&artifacts=0/1][&deleteAll=0/1]
    

    curl -X POST -v -u admin:password "http://artifactory.company.com:8081/artifactory/api/build/service_y?buildNumbers=129,130,131&artifacts=1&deleteAll=1"
    

    在安装了artifactory的同一台服务器上,在Linux Putty中使用上述curl命令时,出现了一个错误。

    * About to connect() to sagrdev3sb12 port 8081
    *   Trying 10.123.321.123... Connection refused
    * couldn't connect to host
    * Closing connection #0
    curl: (7) couldn't connect to host
    

    http://www.jfrog.com/confluence/display/RTF/ArtifactoryREST API#ArtifactoryRESTAPI DeleteBuilds或http://www.jfrog.com/confluence/display/RTF/ArtifactoryREST API#ArtifactoryRESTAPI DeleteItem

    上面的链接显示了他们的-使用示例/使用输出-让我困惑。

    如果我们可以调整这个脚本,为“projectA”(组id)、“service_y”(工件id)和“2.75.0.x”版本保留一个版本并删除所有其他版本,那么下面的链接可能就是答案
    https://github.com/jettro/small-scripts/blob/master/groovy/artifactory/Artifactory.groovy

    我可能需要在Groovy中使用restClient或http Builder(如上面的示例链接和下面的链接中提到的)。
    使用阿蒂工厂的REST API部署jar文件

  • 共有3个答案

    穆毅然
    2023-03-14

    想知道以下内容是否有帮助:

    博客:http://browse.feedreader.com/c/Gridshore/11546011

    脚本: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)
            }
        }
    }
    

    Artifactory REST API类似于(我不确定):
    我看到行:def artifactSearchUri=“API/build/${jobName}/${buildNumber}”

    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}")
    }
    
    鲁城
    2023-03-14

    我有同样的问题,并找到了这个网站。我把这个想法简化成一个python脚本。你可以在githubclean_artifactory.py找到脚本

    如果你有任何问题,请让我知道。我刚才清理了一万多个快照神器!

    其中一些功能:

    1. 干跑
    2. 可以指定时间延迟,在时间范围内最后更新的版本将不会被删除
    3. 指定一个目标组,如com/foo/bar和所有具有in的快照

    希望这有帮助!

    曾枫
    2023-03-14

    最终答案:这个脚本程序脚本/Groovy脚本-包括从两个版本中删除-Jenkins(使用groovyit.delete())和ArtFactory(使用ArtFactory REST API调用)。

    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 ""
    
     类似资料:
    • 我有一个Jenkins构建,它有两个要求。 > maven构建(.war),将war推送到工件上的快照或发布。 还将计划(.xml)文件推送到artifactory上的“计划”存储库-这需要在路径中包含pom版本(来自pom.xml)。 我无法用pom版本替换路径中的$POM-VERSION。到目前为止,我已经尝试了什么 Jenkins Maven Build-我似乎没有用这个选项将计划文件上传到

    • 我希望能够运行SpringBoot:build映像,并让它从本地Artifactory服务器而不是docker中提取paketobuildpack。木卫一。我希望插件使用docker配置文件作为我的凭证,而不是在pom文件中硬编码。 这个作品: 当我在没有上面的docker块的情况下运行时,我得到以下错误: 我的docker/config。json具有以下配置: 我可以成功地运行docker bu

    • 问题内容: 如何从Jenkins GUI中删除构建?我知道我可以从“ jobs”文件夹中删除目录,但是我想从GUI中删除它。是否还可以删除多个版本? 问题答案: 如果您进入要删除的版本,并且拥有删除权限,那么您将在右上角看到“删除此版本”按钮。

    • 我有一个Gradle构建,在Jenkins中运行,并使用Jenkins的Artifactory插件部署到Artifactory。我正在上传一个将文件发送到Artifactory。插件正在上传正确,但它也在上传一个。 如何排除,使其不被部署到Artifactory? 建筑格雷德尔(文物组)

    • 我有两个jenkins作业,第一个作业向artifactory发布一个jar文件,并向工件添加一个属性(提交id)。第二个作业总是从artifactory中提取最新版本的jar,但它还需要与该工件关联的属性。我们正在使用gradle进行构建。有没有办法进入gradle或者我们有其他选择吗? 编辑:作业不是管道的一部分。这意味着它们是独立的作业,第二个作业触发另一个存储库更改。

    • 我有一个Laravel项目,我试图用Storage::delete删除图像,但它似乎不起作用。我的图像位于storage\app\public\uploads文件夹中。我可以上传和编辑图像,但删除不起作用。我使用的是Laravel 8。 产品控制器。PHP PHP 网状物PHP 配置/文件系统。PHP