在组装之后但在安装之前对jar进行后处理(以获得幂等构建)



我们使用Jenkins,它使用md5指纹来识别工件以及工件自上次构建以来是否发生了更改。不幸的是,Maven构建总是生成不同的二进制工件。

因此,我正在研究让Maven为同一组输入文件生成相同的jar工件,无论它们是在哪里和何时构建的,这意味着jar文件中的条目必须进行排序——不仅是在索引中,而且是在写入jar文件的顺序中。

在检查了使用maven汇编插件的maven jar插件后,我的结论是,他们不会在一次写入所有文件之前收集所有要写入内存的文件,而是一次写入一个文件。这意味着最好对生成的jar进行后处理,而不是更改当前行为,这样我就可以在那时对条目进行排序,将时间戳清零,等等。

我不熟悉编写Maven插件,所以我的问题是,我应该如何编写一个插件,让Maven知道如何判断正在进行的工件jar的位置,以及如何将其连接到我的pom.xml中?

(一开始我需要这个来处理jar文件,但war文件也不错(。

如前所述,这可以基于类似于maven-shade-plugin的东西来完成。我继续写了一个简单的插件来添加这个功能——请参阅https://github.com/manouti/jar-timestamp-normalize-maven-plugin(可在中央回购中获得(。

该行为基于shade插件。它由一个名为normalize的单一目标组成,该目标可以绑定到package生命周期阶段,并在项目的POM:中进行配置

<plugins>
    <plugin>
        <groupId>com.github.manouti</groupId>
        <artifactId>jar-timestamp-normalize-maven-plugin</artifactId>
        <version>1.0-SNAPSHOT</version>
        <executions>
            <execution>
                <id>jar-normalize</id>
                <goals>
                    <goal>normalize</goal>
                </goals>
                <phase>package</phase>
            </execution>
        </executions>
    </plugin>
</plugins>

关于插件的一些注意事项:

  1. 构建中的工件通过project#getArtifact()访问,其中projectorg.apache.maven.project.MavenProject

  2. 规范化主要包括三个步骤:

    • 将所有Jar条目的上次修改时间设置为特定的时间戳(默认值为1970-01-01 00:00:00AM,但可以通过-Dtimestamp系统属性进行更改(。

    • 重新排序清单中的属性(按字母顺序(,但始终排在第一位的Manifest-Version除外。

    • pom.properties文件中删除包含时间戳的注释,该时间戳会导致Jar在不同的构建中有所不同。

一旦调用,目标将在原始工件(名为artifactId-version-normalized.jar(旁边生成输出文件,即在project.build.directory目录中。

创建maven插件项目

mvn archetype:generate 
  -DgroupId=sample.plugin 
  -DartifactId=hello-maven-plugin 
  -DarchetypeGroupId=org.apache.maven.archetypes 
  -DarchetypeArtifactId=maven-archetype-plugin

调用该命令,它将生成一个名为MyMojo.java的类的框架项目

execute()方法中编写你的东西,并通过mvn clean install将该插件安装到你的存储库中

然后将它的执行与您的项目联系起来,在您的项目pom.xml

 <build>
    <plugins>
      <plugin>
        <groupId>sample.plugin</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <version>1.0-SNAPSHOT</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>sayhi</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

访问Mojo 内的项目属性

    /**
     * The Maven project.
     *
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    private MavenProject        project; 

然后

project.getProperties("build.directory") 

并读取其他属性以确定您的jar文件打包


请参阅

  • maven:指导java插件开发

我同意创建一个自定义的maven插件似乎是一个更好的选择。我不知道有没有一个现有的插件为您提供了解决方案。

md5校验和(或者我的存储库中的sha-1(是用安装插件生成的,所以你似乎需要扩展它,或者写一个新的插件,在安装阶段后工作。

我有两个关于这个插件的建议:

1(当考虑简单时,这个插件应该:

  • 读取生成的jar:
    • 提取所有条目
    • 排除某些条目(例如MANIFEST.MF(
    • 对剩余条目进行排序
    • 为内存中的每个提取md5
    • 从所有提取的数据中生成一个md5

然而,当考虑在哪里&当独立性:根据.class文件结构Java_class_file,编译后的类文件中包含次要、主要版本条目。所以,如果编译器发生变化,.class文件也会发生变化。在这种情况下,我们需要从这一点检查源代码级别:(因此,如果不能保证复印机版本,这个解决方案将变得毫无用处。


2(作为一个非常肮脏但简单的解决方案,此插件可能只提取模块的pom.xml文件的md5代码。但是您必须保证jar中的每个更改都手动反映到一个次要版本(或内部版本号(。

您可以编写由Groovy-maven插件执行的Groovy脚本,而不是编写自己的插件:

<plugin>
  <groupId>org.codehaus.gmaven</groupId>
  <artifactId>groovy-maven-plugin</artifactId>
    <executions>
      <execution>
        <phase>package</phase>
        <goals>
          <goal>execute</goal>
        </goals>
        <configuration>
          <source>
              import java.util.jar.*
              String fileName = '${project.build.directory}/${project.build.finalName}.jar'
              println "Editing file ${fileName}"
              JarFile file = new JarFile(fileName);
               // do your edit
          </source>
        </configuration>
      </execution>
  </executions>
</plugin>

最新更新