【问题标题】:Avoiding maven repository version collision when using feature branches使用功能分支时避免 Maven 存储库版本冲突
【发布时间】:2016-07-02 18:10:40
【问题描述】:

问题:如何处理 maven 多项目构建的功能分支?

Jenkins 构建和部署这些分支以将开发人员的构建开销降至最低,但开发和功能分支无法构建相同的 maven 版本,否则我们将面临工件和源代码不匹配的风险。

我们有一个脚本可以更改子 pom 中的父版本和根 pom 中的版本。虽然这会在 maven 空间中分离分支,但在合并时会导致额外的工作。

我认为 nexus pro 暂存功能可能会帮助我们避免此要求,并使每个功能分支使用一个特定的 repo,我们在分支删除/合并后很容易删除该 repo。

再次:如何处理多个分支和maven的问题?

【问题讨论】:

  • 我们有一个脚本可以更改子 pom 中的父版本和根 pom 中的版本 为什么不使用maven-release-plugin:branch 目标?它将自动创建分支并更新版本,您可以将其设置为 x.x-my-branch 或类似的东西。
  • 谢谢@Tunaki。自从 pom 被修改后,合并仍然是个问题吗?
  • 是的,换个版本会很痛苦……另见stackoverflow.com/questions/3555160/…(或stackoverflow.com/questions/12197191/…
  • 我们在分支中使用mvn versions:set -DnewVersion=1.2-BRANCH-SNAPSHOT 来保持它的独特性。

标签: maven jenkins maven-3 nexus git-flow


【解决方案1】:

下面的方法怎么样:

  • 使用buildnumber-maven-plugin从git获取信息并填充特定的Maven属性(我们特别感兴趣的是scmBranch属性(即当前的git分支)
  • 使用build-helper-maven-plugin 来检查我们是否在功能分支中(通过regex,不包括masterdevelop 等众所周知的分支)并填充(或不)a新的 Maven 属性,比如 branch.classifier
  • 使用maven-jar-plugin在生成的工件上设置分类器,基于上一步设置的内容,即使用新的branch.classifier属性:如果为空,则不应用分类器(默认行为,应用于develop 分支,例如);否则将以当前分支命名的分类器将被动态应用。

这是一个最小的例子:

<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>buildnumber-maven-plugin</artifactId>
            <version>1.4</version>
            <executions>
                <execution>
                    <phase>validate</phase>
                    <goals>
                        <goal>create</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>build-helper-maven-plugin</artifactId>
            <version>1.10</version>
            <executions>
                <execution>
                    <id>regex-property</id>
                    <goals>
                        <goal>regex-property</goal>
                    </goals>
                    <configuration>
                        <name>branch.classifier</name>
                        <value>${scmBranch}</value>
                        <regex>(^develop)|(^master)|(^release.*)</regex>
                        <replacement></replacement>
                        <failIfNoMatch>false</failIfNoMatch>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.0.2</version>
            <configuration>
                <classifier>${branch.classifier}</classifier>
            </configuration>
        </plugin>
    </plugins>
</build>

下面的 sn-p 基本上是动态填充scmBranch 属性,然后将branch.classifier 设置为其值,仅当不同于developmasterrelease* 时,然后将其设置为分类器。

这种方法的主要优点

  • 不会应用任何 pom 更改,因此根本不会出现合并问题
  • 在同一版本的项目但在不同的分支上工作的 Nexus 没有冲突:分类的工件将有不同的Maven coordinates,即 GAV (groupId, artifactId, version) 变为唯一的 GAVC (+classifier)
  • 这实际上是工件的classifier 属性的一个有意义的用法:

    分类器允许区分从同一个 POM 构建但内容不同的工件。

  • 生成的工件在 Nexus 中将根据其源分支动态不同,因此具有隐式可追溯性:无需开发人员干预(不会出错,隐式约定),无需 CI 作业干预(更易于维护),完全透明
  • 使用分类器,将更容易将分支生成的工件用作 maven 依赖项(例如,在库项目的情况下):我想使用当前正在开发的分支 xxx 上的依赖项

示例
因此,您将生成以下工件:

  • develop 上工作时:例如project-1.0.0-SNAPSHOT.jar(空分类器,因此未应用,由正则表达式处理)
  • featureA 上工作时:例如project-1.0.0-SNAPSHOT-featureA.jar
  • hotfix-JIRA123 上工作时:例如project-1.0.0-hotfix-JIRA123.jar
  • 在处理release-sprint42 时:这取决于你,我添加了这种情况以不应用分支名称,只是因为在这些情况下,我更喜欢为候选发布者设置一个特殊的分类器RC&lt;number&gt;,但仅此而已关于约定/品味/习惯,您也可以在此分支上应用相同的方法,只要不会在 Nexus 上产生冲突。另请注意:当使用 JIRA/Stash/Git 集成时,发布分支名称通常类似于release/v0.1.0,其中/ 字符可能会导致某些操作系统出现问题(但如果确实需要,仍然可以通过进一步的正则表达式替换来修复) .
  • master 上工作时:嘿,没有人应该在master 上工作 :) 这种情况只是作为双重检查,但实际上不是必需的

对这种方法的警告

  • 正如在下面通过 cmets 的讨论中所解释的,如果相关的 Maven 项目已经在使用分类器,甚至更多地通过模块间依赖关系(例如,依赖于来自另一个模块的测试范围类),那么应该仔细测试这种方法,因为它可能有一些缺点
  • 发布包含分支分类器的 &lt;artifactId&gt;.pom 文件可能会与主线构建发生冲突(即覆盖它)

【讨论】:

  • 谢谢。这很漂亮。我将尝试一个小项目,看看我能做什么。棘手的部分将是我构建的 tycho eclipse RCP 部分,但我可能可以使用资源插件来处理这些文件中的标记到属性替换。
  • @PeterKahn 仅供参考:在我工作的公司中,我们实际上现在在我们的交付管道中使用它并且工作正常,Nexus 中没有冲突,开发人员也可以将生成的工件作为 maven 依赖项导入某些分支(罕见情况,但现在可能)不需要更改项目版本,也不需要合并它。
  • 源、测试源、javadoc 和其他通常使用特定名称的分类器呢?这些是否继续在命令行和 IDE 中工作?我希望源和测试源不起作用,并且会为版本而不是特定于分支的版本拉/使用默认值。
  • @PeterKahn 确实使用上面的示例配置将应用您描述的行为,但您也可以配置 maven-source-plugin 以使用 classifier 选项,而不是默认的 sources 值然后有sources${branch.classifier},例如。
  • 对,但是日食不会中断吗?或者它会通过 m2eclipse 得到改变吗?
【解决方案2】:

这不起作用从顶级父级运行时会导致整个构建过程中出现警告和 GC 错误。

理想情况下,我们希望使用版本来区分功能分支和主线,因为这是正常的 maven 方式,分类器操作可能会导致各种问题。

由于 maven 可以为属性使用环境变量,并且我们已经使用脚本初始化构建环境(我们也有可以从分支名称设置环境变量的 git hook 脚本),我们可以使用 env 来控制版本。

<groupID>my.project</groupId>
<artifactID>database</artifactId>
<version>1.2.0${env.BRANCHMODIFIER}-SNAPSHOT</version>

如果在开发我们的脚本时将 BRANCHMODIFIER 设置为 ""
如果在 feature/JIRA-30495 上,我们的脚本将 BRANCHMODIFIER 设置为“.30495”

这在 eclipse 或 Intellij 中如何工作?目前还没有头绪。

【讨论】:

  • A_Di-Matteo 我们对分类器的复杂使用很快变得一团糟。使用#nexus #staging 会导致本地存储库看起来像开发工件,但实际上不是。添加 artifactId 修饰符同样很麻烦。到目前为止,操纵版本似乎是最干净的方法。
  • 这种方法有效,但从树顶运行时会导致警告和堆问题。
  • 这种方法在从本地机器执行构建时可能会出现问题,对吧?
【解决方案3】:

我们使用与 Peter Kahn 类似的技术,在构建之前修改分支的版本。我们的“预备步骤”分为三个步骤:

  1. 执行外壳:echo VERSION=$(echo ${GIT_BRANCH} | sed 's_^.*\/__') &gt; env.properties
  2. 注入环境变量:env.properties
  3. 调用顶级 Maven 目标:versions:set -DgenerateBackupPoms=false -DnewVersion=${VERSION}-SNAPSHOT

我很确定这可以用两个甚至一个步骤来完成,而且只有一步,后面的原理是一样的。

之所以不直接修改分支中pom.xml文件中的版本,确实是merge。使用 SVN,这是可能的(与 --accept-mine-conflict 合并。使用 GIT,这不再存在,因此我们停止更改版本并创建了这个预构建步骤。

【讨论】:

    【解决方案4】:

    对于 Maven ge 3.5.0 试试这个https://maven.apache.org/maven-ci-friendly.html 这是推荐的 Maven 解决方案。 唯一的问题(但不寻常)可能是 Maven 依赖项的数字版本解析。但这只会出现,如果您使用模块的不同 SNAPSHOT 依赖项,无论如何这都是个坏主意。

    【讨论】:

      猜你喜欢
      • 2019-02-28
      • 2010-11-09
      • 2014-10-01
      • 1970-01-01
      • 2023-01-27
      • 1970-01-01
      • 2019-09-26
      • 1970-01-01
      相关资源
      最近更新 更多