如何停止 Maven 的验证阶段重建工件?

Posted

技术标签:

【中文标题】如何停止 Maven 的验证阶段重建工件?【英文标题】:How to stop Maven's verify phase rebuilding the artifact? 【发布时间】:2011-05-18 20:20:50 【问题描述】:

想象一下我有一个使用 Maven 构建的 Java 项目:

一些快速运行的单元测试: 开发人员应在提交前运行 我的 CI 服务器(Hudson,FWIW)应在检测到新提交时运行,在发生故障时提供几乎即时的反馈 一些运行缓慢的自动化验收测试: 开发人员可以选择运行,例如重现和修复故障 我的 CI 服务器应该在成功运行单元测试后运行

这似乎是一个典型的场景。目前,我正在跑步:

“测试”阶段的单元测试 “验证”阶段的验收测试

配置了两个CI作业,都指向项目的VCS分支:

    “提交阶段”,运行“mvn 包”(编译和单元测试代码,构建工件),如果成功,则触发: “自动化验收测试”,运行“mvn verify”(设置、运行和拆除验收测试)

问题在于作业 2 单元测试并重新构建被测工件(因为验证阶段会自动调用包阶段)。出于以下几个原因(重要性降低),这是不可取的:

作业 2 创建的工件可能与作业 1 创建的工件不同(例如,如果同时有新的提交) 将反馈循环延长到做出提交的开发人员(即他们需要更长的时间才能发现他们破坏了构建) 浪费 CI 服务器上的资源

所以我的问题是,如何配置作业 2 以使用作业 1 创建的工件?

我意识到我可以只拥有一个运行“mvn verify”的 CI 作业,它只会创建一次工件,但我希望拥有上述单独的 CI 作业以实现 Farley 风格的部署管道。


如果它对任何人有帮助,这是已接受答案中“项目 2”的完整 Maven 2 POM:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.cake</groupId>
    <artifactId>cake-acceptance</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <name>Cake Shop Acceptance Tests</name>
    <description>
        Runs the automated acceptance tests for the Cake Shop web application.
    </description>
    <build>
        <plugins>
            <!-- Compiler -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.1</version>
                <configuration>
                    <source>$java.version</source>
                    <target>$java.version</target>
                </configuration>
            </plugin>
            <!-- Suppress the normal "test" phase; there's no unit tests -->
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
            <!-- Cargo (starts and stops the web container) -->
            <plugin>
                <groupId>org.codehaus.cargo</groupId>
                <artifactId>cargo-maven2-plugin</artifactId>
                <version>1.0.5</version>
                <executions>
                    <execution>
                        <id>start-container</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>start</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>stop-container</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>stop</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!-- Don't wait for CTRL-C after starting the container -->
                    <wait>false</wait>

                    <container>
                        <containerId>jetty7x</containerId>
                        <type>embedded</type>
                        <timeout>20000</timeout>
                    </container>

                    <configuration>
                        <properties>
                            <cargo.servlet.port>$http.port</cargo.servlet.port>
                        </properties>
                        <deployables>
                            <deployable>
                                <groupId>$project.groupId</groupId>
                                <artifactId>$target.artifactId</artifactId>
                                <type>war</type>
                                <properties>
                                    <context>$context.path</context>
                                </properties>
                            </deployable>
                        </deployables>
                    </configuration>
                </configuration>
            </plugin>
            <!-- Failsafe (runs the acceptance tests) -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <id>integration-test</id>
                        <goals>
                            <goal>integration-test</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>verify</id>
                        <goals>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <includes>
                        <include>**/*Test.java</include>
                    </includes>
                    <skipTests>false</skipTests>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
            <!-- Add your tests' dependencies here, e.g. Selenium or Sahi,
                with "test" scope -->
        <dependency>
            <!-- The artifact under test -->
            <groupId>$project.groupId</groupId>
            <artifactId>$target.artifactId</artifactId>
            <version>$target.version</version>
            <type>war</type>
        </dependency>
    </dependencies>
    <properties>
        <!-- The artifact under test -->
        <target.artifactId>cake</target.artifactId>
        <target.version>0.1.0-SNAPSHOT</target.version>
        <context.path>$target.artifactId</context.path>
        <http.port>8081</http.port>
        <java.version>1.6</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>

请注意,即使这个“测试”项目没有创建工件,它也必须使用某种包装(我在这里使用“jar”),否则在验证阶段不会运行任何测试。

【问题讨论】:

只是为了让那些想要更好地理解该方法的人受益,请参阅 Jez Humble 和 David Farley 的持续交付。 正是促使这一努力的书!物有所值。 【参考方案1】:

所以我的问题是,我该如何配置 作业 2 使用由 工作1?

你不能。

你不需要。 Maven Build lifecycle 的设置方式听起来可以满足您的需求。测试生命周期只会运行快速的junits。打包构建您的最终状态而不运行验证。

您只需要一项 CI 工作。当 CI 运行你的 maven 部署/安装生命周期时,如果 junit 失败,则构建失败,验证脚本将不会执行。

【讨论】:

你说的是真的,但不符合我的(诚然未说明的)要求,即有两个单独的 CI 作业,因为我正在构建一个部署管道并且需要了解哪些修订通过了提交阶段,也通过了自动化验收阶段。我已经澄清了这个问题。【参考方案2】:

尝试两个 Maven 项目。第一个包含构建和单元测试。您将工件安装在本地存储库中。第二个作业运行第二个 maven 项目,它将第一个项目的工件声明为依赖项并运行功能测试。

不确定我刚才描述的场景是否可行,但我认为是。

为了快速改进,您可以使用-Dmaven.test.skip=true 绕过单元测试。如果您将 scm 中代码的修订号传递给第二个作业,您应该能够签出相同的源代码。

您还可以查看 Clone Workspace SCM 插件。这可能会为您提供一些额外的选择。

【讨论】:

我喜欢“两个 maven 项目”的想法;我会试一试。这听起来像是 John Smart 在大约两年前的 CI 幻灯片中推荐的东西,但我不确定细节或它是否仍被认为是最佳实践。 效果很好;我已将第二个项目的 POM 附加到问题中,供任何想尝试的人使用。【参考方案3】:

您可以定义一个仅用于执行集成测试的 Maven 配置文件。我经常这样做。

类似这样的:

<profiles>
    <profile>
        <id>integration</id>
        <activation>
            <activeByDefault>false</activeByDefault>
        </activation>
        <build>
            <plugins>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId><version>2.17</version>
                    <configuration>
                        <skipTests>true</skipTests>
                    </configuration>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId><version>2.4</version>
                    <configuration>
                        <outputDirectory>/tmp</outputDirectory>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

你可以调用它:

mvn verify -Pintegration

不幸的是,无法跳过 WAR 插件,但您可以将其输出发送到不碍事的地方(我使用过 /tmp)。如果你真的想节省毫秒,你也可以让它忽略网络资源(除了web.xml,没有它就行不通)。

您还可以使用配置文件跳过您可能正在运行的任何其他插件,例如程序集插件、Cobertura、PMD 等。

【讨论】:

【参考方案4】:

仅执行集成测试 (as suggested here) 的 Maven 配置文件是不够的。您还需要确保 ma​​ven-compiler-plugin 的配置有 useIncrementalCompilation = false。以这种方式运行配置文件,不会自动重新编译,例如:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.3</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <useIncrementalCompilation>false</useIncrementalCompilation>
    </configuration>
</plugin>

【讨论】:

【参考方案5】:

我知道这已经很长时间了,但这已被很好地索引并且没有一个答案符合所要求的,但我发现了一些有用的东西:

mvn failsafe:integration-test

这会直接运行测试,而无需执行构建项目的所有中间步骤。您可能需要在其后添加failsafe:verify

【讨论】:

以上是关于如何停止 Maven 的验证阶段重建工件?的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot Maven插件

如果头文件被更改然后恢复,如何停止/欺骗cmake不重建?

Maven的验证上启动和每个班级停止春季测试运行

Maven 包签名或您如何信任/验证 Maven Central 工件的完整性和真实性?

如果某个工件已经存在,如何使 Maven 构建失败

如何将时间戳信息添加到 Maven 工件?