Post-process jar after assembly but before installation (to get idempotent builds)

独自空忆成欢 提交于 2019-12-08 15:04:50

问题


We use Jenkins which use md5 fingerprinting to identify artifacts and whether the artifact has changed since the last build. Unfortunately Maven builds always generate binary different artifacts.

Therefore I am looking into making Maven generate the same jar artifact for the same set of input files regardless of where and when they were built, which amongst other things mean that the entries in the jar file must be sorted - not only in the index, but in the order they are written to the jar file.

After examining maven-jar-plugin which use maven-assembly-plugin, my conclusions are that they do not collect all files to be written in memory before writing them all at once, but write one at a time. This mean that it may be better to postprocess the generated jar instead of changing the current behavior so I at that time can sort the entries, zero the timestamps, etc.

I am unfamiliar with writing Maven plugins, so my question is, how should I write a plugin which Maven knows how to tell where the artifact-jar-in-progress is located and how I hook it up in my pom.xml?

(At first I need this to work for jar files, but war files would be nice too).


回答1:


As mentioned, this can be done based on something similar to maven-shade-plugin. I went ahead and wrote a simple plugin to add this capability -- see https://github.com/manouti/jar-timestamp-normalize-maven-plugin (available on the Central repo).

The behavior is based on the shade plugin. It consists of a single goal called normalize which can be bound to the package lifecycle phase and configured in the project's 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>

A few notes about the plugin:

  1. The artifact under build is accessed via project#getArtifact() where project is a org.apache.maven.project.MavenProject.

  2. Normalization consists of mainly three steps:

    • Setting the last modified time of all Jar entries to a specific timestamp (default value is 1970-01-01 00:00:00AM but can be changed via -Dtimestamp system property).

    • Reordering (alphabetically) of attributes in the manifest except for Manifest-Version which always comes first.

    • Removing comments from the pom.properties file which contain a timestamp that causes the Jar to differ from one build to another.

Once invoked, the goal will generate the output file next to the original artifact (named artifactId-version-normalized.jar), i.e. in the project.build.directory directory.




回答2:


To create maven plugin project

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

invoke this command it will generate a skeleton project with a class called MyMojo.java

write your stuff inside execute() method, and install that plugin to your repository by mvn clean install

then attach its execution with your project, in your project 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>

to access project properties inside your Mojo

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

and then

project.getProperties("build.directory") 

and read other properties to determine your jar file packed


See

  • maven: guide-java-plugin-development



回答3:


I agree on creating a custom maven plugin seems like a better option. I dont know about an existing plugin provides solution for what you asked.

md5 checksum (or sha-1 in my repository) is generated with install plugin, so seems like you need to extend this or write a new plugin which works after install phase.

I have 2 suggestions about this plugin:

1) When thinking simple, this plugin should:

  • Read generated jar:
    • Extract all entries.
    • Exclude some entries (e.g. MANIFEST.MF).
    • Sort remaining entries .
    • Extract md5s for each in memory.
    • Generate a single md5 from all of those extracted.

However when considering about where & when independency: Accordig to .class file structure Java_class_file there is minor, major versions entries are held in compiled class files. So if compiler changes, .class files will be changed. In this case we need a check on source code level from this point :( So this solution become useless if there is no guarantee on copiler version.


2) As very dirty but easy solution, this plugin may only extract your module's pom.xml file's md5 code. But you must guarantee each change in your jar reflects to a minor version (or built number) manually.




回答4:


Instead of writing your own plugin you can write a Groovy script that is executed by groovy-maven-plugin:

<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>


来源:https://stackoverflow.com/questions/24961072/post-process-jar-after-assembly-but-before-installation-to-get-idempotent-build

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!