Jenkins构建了一个由许多Maven项目组成的产品? (使用Jenkins Pipeline插件?)

时间:2016-04-14 13:08:51

标签: maven jenkins jenkins-workflow maven-module maven-dependency

我们的产品由许多相互依赖的Maven项目组成。所有这些Maven项目都汇集在一个项目中,提供最终产品。

Maven项目共享相同的生命周期。换句话说,它们不是由具有明确<dependency>更改的单独团队管理,以获取其他项目的较新版本。相反,当有人在其中一个项目中更改某些内容时,结果应直接进入最终产品,而无需进行其他更改。

我们使用Jenkins作为我们的持续集成工具。

我们的主要愿望如下:

  • 无需将所有项目间依赖项复制到Jenkins配置:这些应该位于一个位置,最好是pom.xml个文件。
  • 避免不必要的构建:在SCM更改时,仅构建可能受影响的项目。
  • 如果钻石依赖(C取决于B1和B2,它们都依赖于A),如果最低(A)发生变化,则最终产品(C)应始终使用A的版本,也用于构建/测试B1和B2。

问题:使用Jenkins执行此操作的最佳方法是什么?

我们目前正在考虑使用Jenkins管道插件来使用单个作业,该插件分析Maven依赖关系和SCM更改,决定需要构建什么以及按什么顺序构建,然后实际构建项目。

1 个答案:

答案 0 :(得分:22)

为了满足您的要求,您可以依赖maven多模块构建的许多默认功能以及下面介绍的其他配置。

从你的问题:

  

无需将所有项目间依赖项复制到Jenkins配置:这些应该位于一个位置,最好是pom.xml个文件。

使用aggregator/multi-module pom,您可以为maven构建提供单个入口点(pom.xml文件),然后构建所有已定义的模块:

  

具有模块的项目称为多模块或聚合器项目。模块是此POM列出的项目,并作为一个组执行。 pom打包的项目可以通过将它们列为模块来聚合一组项目的构建,这些模块是这些项目的相对目录。

pom.xml文件,如下所示:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sample</groupId>
    <artifactId>project</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>module-1</module>
        <module>module-2</module>
        <module>module-n</module>
    </modules>
</project>

是一个最小的例子:定义一个模块列表(其他maven项目),这些模块将从这个maven项目开始构建(一个空项目,其唯一目的是构建其他maven项目)。注意pom packaging需要有模块,它告诉maven这个项目只提供一个pom(没有其他工件)。

因此,您可以拥有一个根Maven聚合器项目,该项目将其他maven项目定义为其模块,并且只有一个Jenkins作业构建此入口点。

此外,根据您的问题:

  

避免不必要的构建:在SCM更改时,仅构建可能受影响的项目。

要满足此要求,您可以使用incremental-build-plugin

  

因为它在一个独特项目的模块上寻找修改,所以Maven Incremental Plugin只对多模块项目有所了解。如果检测到对模块的修改,则删除输出目录。

此插件将验证任何pom文件,资源,源,测试源,测试资源是否会在模块中更改以及是否移除其输出目录。因此,为整个多模块项目(在本例中为我们自己的构建)保存相关模块和链中的构建时间。

要启用此机制,您可以在上面的聚合器pom中配置以下内容:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sample</groupId>
    <artifactId>project</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>module-1</module>
        <module>module-2</module>
        <module>module-n</module>
    </modules>

    <properties>
        <skip.incremental>true</skip.incremental>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>net.java.maven-incremental-build</groupId>
                <artifactId>incremental-build-plugin</artifactId>
                <version>1.6</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>incremental-build</goal>
                        </goals>
                        <configuration>
                            <noIncrementalBuild>${skip.incremental}</noIncrementalBuild>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

上面,我们简单地将incremental-build-plugin添加到聚合器构建和属性skip.incremental,它将跳过第一个构建的插件执行,空聚合器1,同时将其启用到模块如下:

<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.sample</groupId>
        <artifactId>project</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>simple-module</artifactId>

    <properties>
        <skip.incremental>false</skip.incremental>
    </properties>
</project>

注意:在示例模块的上面的pom中,我们将聚合器pom文件指向父级,因此将maven inheritance与聚合(经典用法)结合使用,因此具有多模块构建加上由所有已声明模块的公共父提供的公共构建治理(在这种情况下,公共构建治理提供了额外的incremental-build-plugin配置)。此外,每个模块重新配置skip.incremental属性以不跳过上述插件。这是一个诀窍:现在插件只会在模块中执行,而不是在它的根目录中执行(这没有意义,在这种情况下实际上会抛出错误)。

显然,在相关的Jenkins工作中,在其源代码管理部分,我们不必在每次构建时配置全新的签出,否则不会有任何更改可检测的(每次从零开始,这实际上是发布版本的一种很好的做法)。

此外,您的maven命令不应调用clean生命周期,这也会删除每个版本的target,因此也不会检测到任何更改。

此外,从你的问题:

  

如果是钻石依赖&#39; (C取决于B1和B2,它们都依赖于A),如果最低(A)改变,那么最终产品(C)应该总是使用也用于构建/测试B1和B2的A版本

Maven将把这个要求作为多模块构建及其reactor机制的一部分来处理:

  

Maven中处理多模块项目的机制称为reactor。 Maven核心的这一部分执行以下操作:

     
      
  • 收集所有可用的模块
  •   
  • 将项目分类为正确的构建顺序
  •   
  • 按顺序构建所选项目
  •   

默认情况下,反应堆也会创建构建订单,并始终在其依赖/消费者模块之前构建模块。这也意味着最后构建的模块实际上将是负责构建最终工件的模块,可交付产品取决于部分或全部其他模块(例如,负责提供war文件的模块可能是最后一个在多模块webapp项目中构建的人。)

关于Maven的进一步相关阅读和当事情没有改变时的跳过动作:

  • maven-jar-plugin提供默认情况下已启用的forceCreation
      

    即使没有任何内容显示已更改,也需要jar插件来构建新的JAR。默认情况下,此插件会查看输出jar是否存在且输入是否未更改。如果这些条件成立,则插件会跳过jar的创建。

  • maven-compiler-plugin提供了useIncrementalCompilation,但目前只提供了not properly working
  • maven-war-plugin提供recompressZippedFiles选项,该选项也可用于加速构建并避免重新执行某些操作(在这种情况下切换为false):
      

    指示是否应再次压缩添加到战争中的zip存档(jar,zip等)。再次压缩可以缩小存档大小,但会显着延长执行时间   默认true

更新

  

Maven项目共享相同的生命周期。

此要求还会强制使用聚合器/多模块项目,但也可能适用于通过依赖关系链接的不同项目。

  

它们不是由具有明确更改的单独团队管理,以获取其他项目的较新版本。

这一点也强制使用多模块项目。在多模块项目中,模块之间可能有不同的版本,但通常的做法和准则是共享相同的版本,由父项目(聚合器)定义并在其模块中级联。因此,它的集中化和治理将避免错位和错误。

  

当有人更改某个项目中的内容时,结果应该直接进入最终产品,而无需进行其他更改。

同样,这将由多模块项目自动处理。每个使用SNAPSHOT versions的不同项目都会发生同样的情况,因此无需在其消费者项目(负责构建最终产品的项目)中更改依赖项版本。但是,虽然SNAPSHOT版本在开发过程中非常有用(并且推荐),但在交付最终产品时绝对不能使用它们,因为构建可重复性会有危险(也就是说,您可能无法在以后重新构建相同版本因为它依赖于SNAPSHOT版本,因此不是冻结版本)。因此,SNAPSHOT不是银弹解决方案,只能在产品的SDLC的某些阶段使用,而不能作为最终的冷冻解决方案。

更新2
值得同时查看Maven 3.3.1 + 和Java 7 的新Maven Incremental Module Builder

更多详情: