如何从 Maven 存储库中清除旧的依赖项?

Posted

技术标签:

【中文标题】如何从 Maven 存储库中清除旧的依赖项?【英文标题】:How to clean old dependencies from maven repositories? 【发布时间】:2013-10-19 02:12:03 【问题描述】:

我在 .m2 文件夹中有太多文件,maven 存储下载的依赖项。有没有办法清理所有旧的依赖项?例如,如果存在具有 3 个不同版本的依赖项:1、2 和 3,则清理后必须只有 3rd。我如何为 .m2 文件夹中的所有依赖项执行此操作?

【问题讨论】:

只需删除.m2repository 文件夹。编译项目后,它将自动创建。 或者买更大的硬盘,不在乎:) 可能有比等待编译和花钱买硬盘更优雅的解决方案吗? :) 但是说真的,我在虚拟机上远程工作,所以磁盘空间(小)和编译时间(长)很重要。这就是为什么我不能简单地更换硬盘或处理器。所以我需要一种更有效地使用它的方法。 如果您打开了 IDE 以及您最近的所有项目,文件系统锁定将阻止您删除正在使用的 jars 如何通过pom文件实现依赖的清理? 【参考方案1】:

问这个问题已经 6 年多了,但我仍然没有找到任何工具来令人满意地清理我的存储库。所以我自己用 Python 写了一个来摆脱旧的本地人工制品。也许它对其他人也有用:

repo-cleaner.py:

from os.path import isdir
from os import listdir
import shutil
import semver

import Constants

# Change to True to get a log of what will be removed
dry_run = False


def check_and_clean(path):
    files = listdir(path)
    only_files = True
    for index, file in enumerate(files):
        if isdir('/'.join([path, file])):
            only_files = False
        else:
            files[index] = None
    if only_files:
        return

    directories = [d for d in files if d is not None]
    latest_version = check_if_versions(directories)
    if latest_version is None:
        for directory in directories:
            check_and_clean('/'.join([path, directory]))
    elif len(directories) == 1:
        return
    else:
        print('Update ' + path.split(Constants.m2_path)[1])
        for directory in directories:
            if directory == latest_version:
                continue
            print(directory + ' (Has newer version: ' + latest_version + ')')
            if not dry_run:
                shutil.rmtree('/'.join([path, directory]))


def check_if_versions(directories):
    if len(directories) == 0:
        return None
    latest_version = ''
    for directory in directories:
        try:
            current_version = semver.VersionInfo.parse(directory)
        except ValueError:
            return None
        if latest_version == '':
            latest_version = directory
        if current_version.compare(latest_version) > 0:
            latest_version = directory
    return latest_version


if __name__ == '__main__':
    check_and_clean(Constants.m2_path)

Constants.py(编辑以指向您自己的本地 Maven 存储库):

# Paths
m2_path = '/home/jb/.m2/repository/'

确保您已安装 Python 3.6+,并且已将 semver 包安装到您的全局环境或 venv(如果缺少,请使用 pip install semver)。

使用python repo-cleaner.py 运行脚本。

它会在您配置的本地 Maven 存储库中递归搜索(通常为 ~/.m2/repository),如果找到不同版本所在的目录,则会删除所有版本,但最新版本除外。

假设您在本地 Maven 存储库中的某处有以下树:

.
└── antlr
    ├── 2.7.2
    │   ├── antlr-2.7.2.jar
    │   ├── antlr-2.7.2.jar.sha1
    │   ├── antlr-2.7.2.pom
    │   ├── antlr-2.7.2.pom.sha1
    │   └── _remote.repositories
    └── 2.7.7
        ├── antlr-2.7.7.jar
        ├── antlr-2.7.7.jar.sha1
        ├── antlr-2.7.7.pom
        ├── antlr-2.7.7.pom.sha1
        └── _remote.repositories

然后脚本删除antlr的2.7.2版本,剩下的是:

.
└── antlr
    └── 2.7.7
        ├── antlr-2.7.7.jar
        ├── antlr-2.7.7.jar.sha1
        ├── antlr-2.7.7.pom
        ├── antlr-2.7.7.pom.sha1
        └── _remote.repositories

任何旧版本,即使是您积极使用的版本,都将被删除。它可以使用 Maven(或其他管理依赖项的工具)轻松恢复。

通过设置dry_run = True,您可以在不实际删除的情况下获取将要删除的内容的日志。输出将如下所示:

    update /org/projectlombok/lombok
    1.18.2 (newer version: 1.18.6)
    1.16.20 (newer version: 1.18.6)

这意味着 lombok 的 1.16.20 和 1.18.2 版本将被删除,而 1.18.6 将保持不变。

以上文件的最新版本可以在我的github找到。

【讨论】:

@ᴠɪɴᴄᴇɴᴛ 是的,脚本非常简化,因为它只在一个场景中为我服务。感谢您在拉取请求中改进它,因为这是您的工作,您也想编辑答案吗?【参考方案2】:

我确实花了几个小时研究这个问题和答案,其中许多都依赖于atime(这是 UNIX 系统上的最后访问时间),这是一个不可靠的解决方案,原因有两个:

    大多数 UNIX 系统(包括 Linux 和 macOS)最多不定期更新 atime,这是有原因的:atime 的完整实现意味着整个文件系统将因必须更新而变慢(即写入磁盘)atime 每次读取文件时,此外,如此极端数量的更新会很快磨损现代的高性能 SSD 驱动器 在 CI/CD 环境中,用于构建 Maven 项目的 VM 将从共享存储中恢复其 Maven 存储库,这反过来又会将 atime 设置为“最近”值

因此,我创建了一个 Maven 存储库清理器,并在 https://github.com/alitokmen/maven-repository-cleaner/ 上提供了它。 bash maven-repository-cleaner.sh 脚本有一个函数 cleanDirectory,它是一个循环遍历 ~/.m2/repository/ 的递归函数,并执行以下操作:

当子目录不是版本号时,会深入该子目录进行分析 当一个目录有看起来是版本号的子目录时,它只会删除所有较低的版本

在实践中,如果您有如下层次结构:

artifact-group artifact-name 1.8 1.10 1.2

...maven-repository-cleaner.sh 脚本将:

    导航到artifact-groupartifact-group 中,导航到artifact-nameartifact-name中,删除子文件夹1.81.2,因为1.10优于1.21.8

因此这与Andronicus 和Pavan Kumar 提供的解决方案非常相似,不同之处在于它是作为Shell 脚本编写的。要在您的 CI/CD 平台(或任何其他形式的 UNIX 系统)上运行该工具,只需在构建开始或结束时使用以下三行:

wget https://raw.githubusercontent.com/alitokmen/maven-repository-cleaner/main/maven-repository-cleaner.sh
chmod +x maven-repository-cleaner.sh
./maven-repository-cleaner.sh

【讨论】:

【参考方案3】:

给定一个 maven 项目的 POM 文件,您可以使用 Apache Maven Dependency Plugin 删除其在本地存储库(默认为 ~/.m2/respository)中的所有依赖项。

它包含dependency:purge-local-repository 功能,可从本地存储库中删除项目依赖项,并可选择重新解析它们。

要清理本地依赖项,您只需使用可选参数 reResolve 并将其设置为 false,因为它默认设置为 true。

这个命令行调用应该可以工作:

mvn dependency:purge-local-repository -DreResolve=false

【讨论】:

好 :) 但是这种清理依赖仅适用于当前项目,而不是所有存储库。 这是真的!为了清理整个存储库,我将手动从 ./m2/repository 中删除目录,正如之前或在较新版本的 Nexus(2.6.4-02 之后)中所评论的那样。它们提供了计划任务 link 从存储库中删除版本功能盒子外面。它也可能有用【参考方案4】:

您需要复制项目所需的依赖项。 有了这些,请清除嵌入到<dependencies> 标签中的所有<dependency> 标签 来自项目中的 POM.XML 文件。

保存文件后,您将不会在 Libraries 中看到 Maven 依赖项。 然后请粘贴您之前复制的那些<dependency>

需要的 jars 会被 Maven 自动下载,你也可以在 保存文件后生成的Maven Dependencies Libraries

谢谢。

【讨论】:

【参考方案5】:

我想出了一个实用程序并托管在 GitHub 上,用于清理本地 Maven 存储库中的旧版本库。该实用程序在默认执行时会删除所有旧版本的工件,只留下最新版本。可选地,它可以删除所有快照、源、javadocs,并且可以在此过程中强制/排除组或工件。此跨平台还支持基于上次访问/下载日期的基于日期的删除。

https://github.com/techpavan/mvn-repo-cleaner

【讨论】:

【参考方案6】:

简答 - 删除了user.home 中的 .m2 文件夹。例如。在 Windows 10 用户主页中是 C:\Users\user1。使用mvn clean package 重新构建您的项目。只有那些项目需要的依赖项才会保留。

长答案 - .m2 文件夹就像一个普通文件夹,文件夹的内容是由不同的项目构建的。我认为没有办法自动找出哪个库是“旧的”。事实上,老是一个模糊的词。在项目中使用以前版本的库可能有很多原因,因此无法确定哪个未使用。

您所能做的就是删除 .m2 文件夹并重新构建所有项目,然后该文件夹将自动构建所有必需的库。

如果您只关心要在所有项目中使用的库的特定版本;重要的是项目的 pom 也应该更新到最新版本。即,如果不同的 POM 引用不同版本的库,则所有内容都将下载到 .m2 中。

【讨论】:

“因此无法确定哪个未使用”。我不需要这个决定,我只需要留下新版本。 然后删除 .m2 文件夹,然后确保所有项目在 pom.xml 中只有新版本的 jar 条目。重新构建项目。 .m2 文件夹将仅保留最新版本。 delete .m2 它会导致删除所有依赖项并从非常慢的存储库中下载新的依赖项。 对于上述问题 - 一种适合所有人的解决方案 - 我们可以采用上述方式。对于缓慢的问题,我建议该项目应咨询本地存储库(一种组织中的产品存储库设置);如果无法从公共存储库下载。在某个时间点,建议最终将工件上传到本地存储库 - 如果它在所有项目中如此广泛使用。【参考方案7】:

    下载项目的所有实际依赖项

    find your-projects-dir -name pom.xml -exec mvn -f '' dependency:resolve
    

    将本地 maven 存储库移动到临时位置

    mv ~/.m2 ~/saved-m2
    

    将保存的存储库中的所有文件 maven-metadata-central.xml* 重命名为 maven-metadata.xml*

    find . -type f -name "maven-metadata-central.xml*" -exec rename -v -- 's/-central//' '' \;
    

    要将本地存储库的修改副本设置为镜像,请创建目录 ~/.m2 和文件 ~/.m2/settings.xml,其内容如下(替换 user 使用您的用户名):

    <settings>
     <mirrors>
      <mirror>
       <id>mycentral</id>
       <name>My Central</name>
       <url>file:/home/user/saved-m2/</url>
       <mirrorOf>central</mirrorOf>
      </mirror>
     </mirrors>
    </settings>
    

    再次解决您的项目依赖关系:

    find your-projects-dir -name pom.xml -exec mvn -f '' dependency:resolve
    

    现在您拥有本地 maven 存储库,其中包含最少的必要工件。从配置文件和文件系统中删除本地镜像。

【讨论】:

试过这个,仍然得到:“在当前项目和插件组 [org.apache.maven.plugins, org.codehaus.mojo] 中找不到前缀'依赖'的插件存储库 [local (/home/user/.m2/repository), mycentral (file:/home/user/saved-m2/)]"【参考方案8】:

只需清理 .m2-->repository 文件夹下的所有内容。当你构建项目时,所有依赖项都会加载到这里。

在您的情况下,您的项目之前可能使用任何依赖项的旧版本,现在版本已升级。所以最好清理 .m2 文件夹并使用 mvn clean install 构建您的项目。

现在,最新版本模块的依赖项将下载到此文件夹中。

【讨论】:

除非您将旧版 jar 手动添加到存储库中,或者 Internet 上不再提供依赖项。这个答案有点危险……至少先备份!【参考方案9】:

我也想从我的 Maven 存储库中删除旧的依赖项。我想过只运行弗洛里安的答案,但我想要一些我可以一遍又一遍地运行而不会记住很长的 linux sn-p 的东西,我想要一些具有一点可配置性的东西——更多的是一个程序,而不是一个链unix 命令,所以我采用了基本思想并将其制作成一个(相对较小的)Ruby 程序,它根据上次访问时间删除旧的依赖项。

它不会删除“旧版本”,但是由于您实际上可能有两个不同的活动项目,它们具有两个不同版本的依赖项,所以无论如何我都不会这样做。相反,就像 Florian 的回答一样,它删除了最近未访问过的依赖项。

如果你想尝试一下,你可以:

    访问GitHub repository 克隆存储库,或下载源代码 可以选择检查代码以确保它不是恶意的 运行bin/mvnclean

有一些选项可以覆盖默认的 Maven 存储库、忽略文件、设置阈值日期,但您可以在 GitHub 上的 README 中阅读这些选项。

在我完成更多工作后,我可能会在某个时候将它打包为 Ruby gem,如果您已经安装并运行了 Ruby,这将简化事情 (gem install mvnclean; mvnclean)。

【讨论】:

很高兴听到这个消息。我想我应该费心把它变成宝石。 看来我说得太早了。我不知道这种模式——但一些罐子被删除了,尽管我知道我最近用过它们。但是,与此同时,一些人仍然存在。 嗯。好吧,如果你能给我更多的细节,我很乐意调查一下。如果你做到了这一点,请在 GitHub 上提交一个问题供我跟踪。似乎对我来说工作相当一致,但可能有一些特定于 Cygwin 的东西我需要研究,例如。您是否在同意之前查看了它输出的列表,或者它太长而不值得审查?如果您确实可以再次尝试,如果我将上次使用的日期添加到摘要中会有所帮助吗? (github.com/geoffreywiseman/mvnclean/issues/10) 是的,您可以添加一些选项来转储调试输出(jar:上次访问日期)...我已经订阅了您打开的问题。 已更新;看看这个问题,描述你的一些选择。【参考方案10】:

如果您使用的是 Unix,则可以使用其中文件的访问时间。只需为您的文件系统启用访问时间,然后运行您想要保留依赖项的所有项目的干净构建,然后执行以下操作(未测试!):

find ~/.m2 -amin +5 -iname '*.pom' | while read pom; do parent=`dirname "$pom"`; rm -Rf "$parent"; done

这将找到最后一次访问超过 5 分钟的所有 *.pom 文件(假设您开始构建最多 5 分钟前)并删除它们的目录。

在 rm 之前添加“echo”以进行“试运行”。

【讨论】:

在 OSX 上(也可以在 GNU 工具上工作)find ~/.m2/repository/ -atime +30 -iname '*.pom' -print0 | while read -d '' -r pom; do echo rm -rf "$(dirname $pom)"; done 其中atime 以天为单位(与amin 以分钟为单位相比) 我在 OSX 上使用 find ~/.m2 -atime +1w -iname '*.pom' | while read pom; do parent=$(dirname "$pom"); rm -rf "$parent"; done。应该在其他 unix 上也能正常工作:) 我刚刚爱上了“查找”

以上是关于如何从 Maven 存储库中清除旧的依赖项?的主要内容,如果未能解决你的问题,请参考以下文章

如何强制 IntelliJ 中的 Maven 从本地 .m2 存储库中提取特定依赖项

Maven 可以重新签署依赖项吗?

Gradle在本地Maven存储库中找不到现有的依赖项

Maven 检查存储库中更新的依赖项

Maven 检查存储库中更新的依赖项

将所有 Maven 依赖项下载到不在存储库中的目录?