【问题标题】:How to make Jenkins consider two different builds of a Maven -SNAPSHOT jar artifact identical as part of Continuous Delivery?如何让 Jenkins 将 Maven -SNAPSHOT jar 工件的两个不同构建视为持续交付的一部分?
【发布时间】:2013-10-11 06:37:07
【问题描述】:

编辑:这是关于使用 Maven 进行持续交付并使用 Jenkins 进行编排。 Maven 绝对不是为此而设计的,这个问题是我们在不使用 Maven 版本的情况下获得高效工作流程的一部分。感谢您的帮助。


我们在主要版本中使用 Maven -SNAPSHOT,以确保客户始终获得该给定版本的最新代码,这很好用。出于技术原因,我们有两个独立的 Maven 作业 - 一个用于将源代码编译为 jar,另一个用于将适当的 jar 组合到给定的部署中。这也很有效。

然后我们让 Jenkins 编排何时调用各个步骤,这有点棘手,因为如果我们在第一步中执行正常的mvn clean install,这意味着所有快照工件都会重新编译,这在turn 使 Jenkins 认为所有快照 已更改(因为它们的指纹 - 也称为 MD5 校验和 - 已更改)即使用于生成工件的源没有更改,从而触发 all下游构建,而不仅仅是那些依赖确实改变的。

到目前为止,我已将这些内容确定为在构建之间有所不同:

  • META-INF/maven/.../pom.properties(因为它包含时间戳)
  • META-INF/MANIFEST.MF(包含 JDK 和用户)
  • jar 文件中的时间戳

我首先找到了解决这两个问题的方法,但后者有点困难。似乎 AbstractZipArchiver(在 zipFile() 和 zipDir() 中完成所有工作)并不是为了允许对存档的生成方式进行任何类型的扩展。

目前我可以想象四种方法(但非常欢迎更多想法):

  • 创建当前 maven-jar-plugin 实现的派生,允许 timestamp=<number> 属性,然后将其用于插入到 jar 文件中的所有条目。如果未设置,则保留当前行为。
  • 修改 Jenkins 指纹识别方案,使其了解 jar 文件并且只查看条目内容,而不查看它们的元数据。
  • 将插件附加到prepare-package 阶段,负责touch 使用特定时间戳的文件。这要求当时所有文件都存在(意味着不能允许 jar 插件接触 MANIFEST.MF 文件)
  • 将一个额外的插件附加到“包”阶段,它会重写完成的 jar 文件,将过程中的所有 zip 条目时间戳归零。

同样,我们的目标是让 maven SNAPSHOT 工件完全独立于时间,因此在相同的来源下,您将获得具有相同 MD5 校验和的工件。不过,我也相信这可能对发布版本有益。

我应该如何处理这个问题?

【问题讨论】:

  • 您正试图强制快照表现得像发布一样。给你的客户发布版本不是更简单,然后 Maven/Jenkins 等将毫不费力地工作吗?
  • RELEASE 工件在被 Jenkins 重建时的行为方式相同。
  • 您能解释一下为什么要重建未更改的快照吗?如果您有一个创建一百万个不同快照的超大型项目,那么单独构建模块或将项目拆分为更小的项目可能是有意义的。在我看来,您试图开发一种解决方法而不是实际解决根本原因。
  • @PeterSchuetze 如果您有很多 maven 项目,每个项目都对应于一个应用程序发送给客户或一个库,这些应用程序中的几个应用程序使用它们,通常将它们组合在一个父项目下。就像现在一样,最简单的方法是将 Jenkins 指向父 pom,然后“mvn clean install”会清理 all 模块并安装 all 模块。 “正确的做法”是为每个模块设置一个 Jenkins 作业,但这非常繁琐——对我们来说可能有一百多个作业。

标签: java maven jar jenkins continuous-delivery


【解决方案1】:

根据我的评论,我仍然认为答案是不做任何你建议的事情,而是使用 releases 而不是 snapshots 来处理你的工件实际上是向客户发布。

你描述的问题是:

  • 您的多模块项目需要很长时间才能构建,因为您有 100 多个模块,
  • 您有两个您认为应该相同的快照工件(因为源代码和元数据在构建时是相同的),但它们具有不同的校验和。

我在 Maven 方面的经验告诉我,如果您尝试并坚持“Maven 方式”,那么开箱即用的工具会很好地为您工作,但如果您偏离了方向,那么您将度过一段糟糕的时光。不幸的是,Maven 方式有时难以捉摸:-)

Maven 中的多模块项目非常有用,当您拥有具有代码不同的代码的模块系列时,例如你有一个包含一堆接口的模块,以及一些提供实现的兄弟模块。在一个多模块项目中拥有十几个模块是不寻常的。所有模块都应该共享父模块的版本号(Maven 没有强制执行这一点,我认为这很令人困惑)。

当您构建多模块项目的快照版本时,即使特定模块中的代码没有更改,也会构建所有模块的快照。因此,您可以查看存储库中的一系列模块,并知道在编译时满足模块间代码引用。

例如,在域模型模块中,您可能有一个接口:

public interface Student {
    void study();
}

在一些兄弟模块中,它们会在其 POM 中声明对域模型的编译范围依赖项,您可能有实现。

如果您要更改领域模型模块中的接口:

public interface Student {
    void study();
    void drink(Beer beer);
}

并重新构建多模块项目,构建将失败。 依赖模块将无法构建,即使它们的代码和 POM 保持不变。在多模块项目中,只有在所有子模块都成功构建时,您才安装或部署工件,因此重建快照通常是非常可取的 - 它告诉您有关模块间依赖关系的一些信息。

如果:

  • 您的模块数量过多,和/或
  • 这些模块不能合理地共享相同的版本号,和/或
  • 您不需要对模块之间的代码引用做任何保证,

那么你的模块化是不正确的。不要使用多模块项目作为构建系统(你有 Jenkins),而是使用它来表达代码模块之间的关系。

在你的评论中,你说:

在被 Jenkins 重建时,RELEASE 工件的行为方式相同。

发布工件的要点是您重建它们 - 它们是确定的!如果您使用 Artifactory 之类的东西,您会发现您不能多次部署发布工件 - 如果您尝试它,您的 Jenkins 作业应该会失败。

这是 Maven 的基本原则。 Maven 的目标之一是,如果不同工作站上的两个开发人员尝试相同的版本,他们将构建功能相同的工件。如果您正在构建一个表达依赖关系的工件(可能是出于编译目的,或者因为它被组装成 .war 等),那么:

  • 如果依赖项是快照,Maven 可能会从存储库中寻找更新的版本。
  • 如果依赖项是版本,则假定本地存储库中的版本是确定的。

如果您可以重新构建发布工件,则可能会导致两个开发人员在他们的存储库中拥有不同的版本,并且根据您使用的工作站的不同,您的构建版本也会不同。不要这样做。

另一个关键细节是发布工件不能依赖于快照工件,同样,您将失去各种保证。

发布是确定的,听起来您希望您的程序集依赖于确定的工件。 Jenkins 让多模块项目的标记和发布变得非常简单。

总结:

  • 检查您的模块化:一个庞大的多模块项目没有用处。
  • 如果您不想不断重建快照,则需要进行发布。
  • 永远不要向您的客户发布快照。
    • 遵循组装项目的依赖关系图并发布任何快照。
    • 发布组装项目,升级您的次要版本。
    • 确保您的客户在通信中参考您的程序集的完整版本号。

【讨论】:

  • 根据您的 cmets,我已经澄清了这个问题,因为这是使用 Maven 进行持续交付的更大努力的一部分。基本上每个构建都可以交给客户,我们对何时接受给定构建以返回到所有 STEP 1 工件并释放它们并重新构建,然后在复制之前重新测试最终位不感兴趣。 Maven 的思维方式非常复杂。
  • 请注意,部署阶段不会报告回“上游”。换句话说,我们无法将特定的提交宣传为事后交付。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-15
  • 2021-06-25
  • 1970-01-01
  • 2013-11-12
  • 2016-10-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多