75

I want to generate a zip file that will update an application with maven. The zip will be hosted on a server and I am using the assembly plugin to generate the zip. However I would like maven to automatically generate a text file that stores the current version number outside the zip. How is this possible?

EDIT: I successfully did it using the maven Assembly Plugin and two descriptors to create two custom assemblies. One has a directory-single goal and it just creates a folder with the updated version.txt based on filtering. Then another one with a single goal actually packages the zip file. This seems to be very inelegant and I guess it will not properly update the maven repo with the whole updated folder. If there is a better way to do this, please let me know.

Bodo Thiesen
  • 2,476
  • 18
  • 32
sanz
  • 1,232
  • 1
  • 9
  • 17

14 Answers14

108

Sure. Create a text file somewhere in src/main/resources, call it version.txt (or whatever)

File content:

${project.version}

now in your pom.xml, inside the build element, put this block:

<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
      <includes>
        <include>**/version.txt</include>
      </includes>
    </resource>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>false</filtering>
      <excludes>
        <exclude>**/version.txt</exclude>
      </excludes>
    </resource>
    ...
  </resources>
</build>

after every build, the file (which you can find in target/classes) will contain the current version.

Now if you want to move the file somewhere else automatically, you are probably going to need to execute an ant task via the maven-antrun-plugin.

Something like this:

  <build>
    ...
    <plugins>
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
         <version>1.4</version>
         <executions>
          <execution>
            <phase>process-resources</phase>
            <configuration>
               <tasks>
                 <copy file="${project.build.outputDirectory}/version.txt"
                   toFile="..." overwrite="true" />
              </tasks>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
   </plugins>
 </build>
Madbreaks
  • 19,094
  • 7
  • 58
  • 72
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • For some reason the first block doesn't seem to put the version in target/classes. The second block of code seems to work if I create the version.txt. – sanz Aug 20 '10 at 17:07
  • Is your project a webapp? Then resource filtering works a bit differently. I'll update my answer in a few hours (dinner first) :-) – Sean Patrick Floyd Aug 20 '10 at 17:15
  • No but the project I am working on is just a maven module with a parent project that contains the whole application. – sanz Aug 20 '10 at 17:28
  • 4
    @SeanPatrickFloyd would you mind to explain why I need filtering=false for excludes and filtering=true for includes? What does this mean? – Karussell Nov 08 '12 at 16:15
  • 1
    @Karussell it means when you don't want to filter every resource you need two separate runs, one with filtering, one without. and what's included in one of the runs needs to be excluded in the other, obviously – Sean Patrick Floyd Nov 08 '12 at 17:00
  • 4
    you don't have to move the file with a whole separate operation. You can specify ``. – Adam Dec 06 '17 at 16:12
  • @AbhishekSaini I edited the question - when it's approved, you'll see `targetPath`. It goes inside the `resource` element. – Adam Jan 11 '18 at 10:50
  • Why are you putting the version.txt file in there twice as resource? I think I get the filtering and include. But why the no filtering and exclude part? – dingalapadum Jul 17 '18 at 14:08
  • 1
    @dingalapadum this configuration means that everything except this file should get copied without filtering (second block) and only the text file should be filtered while copying (second block). – Sean Patrick Floyd Jul 17 '18 at 18:19
  • 1
    Alternatively, you may follow the [documentation](https://maven.apache.org/plugins/maven-resources-plugin/examples/filter.html) and store all the filtered resources in `src/main/resources-filtered`, then you enable `` only for that directory to avoid the ``/`` duplication. – otterrisk Nov 02 '18 at 15:09
  • 2
    some how I am seeing different issue: There is version.txt in targes/classes but, {project.version} is not getting replaced with actual version from pom file. – Pawan Jan 11 '19 at 18:59
  • 2
    It would be good solution if it did not use ant run plugin. Since it is using ant run plugin it is enough to define echo task without resource filtering. – JJ Roman May 18 '20 at 21:22
  • 1
    Please note that Spring Boot redefines the token filter to be @ instead of the default ${*}. ref: https://docs.spring.io/spring-boot/docs/1.3.0.RELEASE/reference/html/howto-properties-and-configuration.html#howto-use-short-command-line-arguments – karl li Mar 06 '22 at 02:38
17

Use standard META-INF\MANIFEST.MF (Then you can use Java code getClass().getPackage().getImplementationVersion() to get version)

For .war use this configuration:

<plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.1</version>
    <configuration>
        <archive>                   
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
        </archive>
    </configuration>
</plugin>

That will add manifest during build, or you can call mvn war:manifest

See also How to get package version at running Tomcat?

Community
  • 1
  • 1
Paul Verest
  • 60,022
  • 51
  • 208
  • 332
14

What you are referring to is called filtering

You need to enable filtering on a particular resource, and then use ${project.version} which will be substituted as part of your build

Lambart
  • 1,985
  • 2
  • 21
  • 37
Jon Freedman
  • 9,469
  • 4
  • 39
  • 58
12

in Maven 3, Use Sean's answer to create your version.txt file, (mine is shown here, along with build date and active profile):

${project.version}-${profileID}
${buildDate}

adding property profileID to each of the profiles, e.g.:

<properties>
    <profileID>profileName</profileID>
</properties>

Use Maven copy-resources to copy the file to an easier to reach directory such as ${basedir} or ${basedir}/target:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>3.0.2</version>
    <executions>
        <execution>
            <id>copy-resources</id>
            <phase>validate</phase>
            <goals>
                <goal>copy-resources</goal>
            </goals>
            <configuration>
                <outputDirectory>${basedir}</outputDirectory>
                <resources>
                    <resource>
                        <directory>${basedir}/target/.../[version.txt dir]/version.txt</directory>
                        <includes>
                            <include>version.txt</include>
                        </includes>
                        <filtering>true</filtering>
                    </resource>
                </resources>
            </configuration>
        </execution>
    </executions>
</plugin>

output looks like this:

1.2.3-profileName
yymmdd_hhmm
Kin Cheung
  • 870
  • 10
  • 20
ekangas
  • 843
  • 1
  • 10
  • 17
  • 2
    I needed to add the following configuration, hence this is complementary: ${*} As indicated in following post: https://stackoverflow.com/questions/5340361/replace-task-in-maven-antrun-plugin?answertab=active#tab-top – darkstar_mx Mar 02 '18 at 23:34
  • @darkstar_mx great followup, this is exactly what my configuration was missing – Lev Kolomazov Sep 13 '21 at 13:20
12

For a Spring Boot application, follow the accepted answer from above however substituting

${project.version}

with

@project.version@

Here's the link to the documentation https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.3-Release-Notes#maven-resources-filtering

Vuong Ly
  • 141
  • 1
  • 3
7

I just did this with an ant task.

<echo file="version.txt">${project.version}</echo>
Dr.Octoboz
  • 143
  • 1
  • 5
  • This will replace the full file. – Jerad Rutnam Mar 08 '18 at 09:48
  • 1
    Without further context, question was about Maven, not Ant... ?!? – Betlista Jul 03 '19 at 07:44
  • Using the `antrun-plugin` example from @sean-patrick-floyd's answer, and this answer, worked great for me. I specifically wrote `${project.version}` which drops the version file inside the resources directory to be included in the resulting JAR file. – Guss Nov 17 '19 at 10:19
7

You could also use a Groovy script to produce a version info file. I like this method more because you don't have to exclude stuff in the assembly-plugin's descriptor. You can also use this method to optionally include stuff only available if you are building from Jenkins/Hudson (e.g. check oug BUILD_ID etc...).

So you would have a file-generating groovy script in pom.xml like this:

  <plugin>
    <groupId>org.codehaus.mojo.groovy</groupId>
    <artifactId>groovy-maven-plugin</artifactId>
    <version>1.0-beta-3</version>
    <executions>
      <execution>
        <phase>test</phase>
        <goals>
          <goal>execute</goal>
        </goals>
        <configuration>
          <source>
        <![CDATA[
        println("==== Creating version.txt ====");
        File mainDir = new File("src/main");
        if(mainDir.exists() && !mainDir.isDirectory()) {
            println("Main dir does not exist, wont create version.txt!");
            return;
        }
        File confDir = new File("src/main/conf");
        if(confDir.exists() && !confDir.isDirectory()) {
            println("Conf dir is not a directory, wont create version.txt!");
            return;
        }
        if(!confDir.exists()) {
            confDir.mkdir();
        }
        File versionFile = new File("src/main/conf/version.txt");
        if(versionFile.exists() && versionFile.isDirectory()) {
            println("Version file exists and is directory! Wont overwrite");
            return;
        }
        if(versionFile.exists() && !versionFile.isDirectory()) {
            println("Version file already exists, overwriting!");
        }
        println("Creating Version File");
        BufferedWriter writer = new BufferedWriter(new FileWriter(versionFile));

        writer.write("groupId = ${project.groupId}");
        writer.newLine();
        writer.write("artifactId = ${project.artifactId}");
        writer.newLine();
        writer.write("version = ${project.version}");
        writer.newLine();
        writer.write("timestamp = ${maven.build.timestamp}");

        String buildTag = "";
        String buildNumber = "";
        String buildId = "";
        try {
            buildTag = "${BUILD_TAG}";
            buildNumber = "${BUILD_NUMBER}";
            buildId = "${BUILD_ID}";

            writer.write("BUILD_TAG = " + buildTag + "\n");
            writer.write("BUILD_NUMBER = " + buildNumber + "\n");
            writer.write("BUILD_ID = " + buildId + "\n");

        } catch (Exception e) {
            println("============= Could not find BUILD_TAG probably this is not a Jenkins/Hudson build ===========");
        }

        writer.close();
        ]]>
          </source>
        </configuration>
      </execution>
    </executions>
  </plugin>

And then your assembly plugin plugin in pom.xml that would look like this:

  <plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.2.1</version>
    <!-- Produce the all-dependencies-included jar for java classloaders -->
    <executions>
      <execution>
        <id>all</id>
        <phase>package</phase>
        <goals>
          <goal>single</goal>
        </goals>
        <configuration>
          <finalName>${project.artifactId}</finalName>
          <descriptors>
            <descriptor>dist-all.xml</descriptor>
          </descriptors>
        </configuration>
      </execution>
    </executions>
  </plugin>

And finally your assembly descriptor dist-all.xml would look like this:

<?xml version="1.0" encoding="UTF-8"?>
<assembly>
  <id>all</id>
  <formats>
    <format>dir</format>
    <format>zip</format>
  </formats>
  <includeBaseDirectory>false</includeBaseDirectory>
  <fileSets>
    <fileSet>
      <directory>target</directory>
      <outputDirectory></outputDirectory>
      <includes>
        <include>*.jar</include>
      </includes>
    </fileSet>
    <fileSet>
      <directory>src/main/conf</directory>
      <outputDirectory></outputDirectory>
      <includes>
        <include>**</include>
      </includes>
    </fileSet>
  </fileSets>
</assembly>
Lorenzo Sciuto
  • 1,655
  • 3
  • 27
  • 57
DeusAquilus
  • 79
  • 1
  • 1
2

I prefer the write-properties-file-maven-plugin, because I don't want all maven-project-properties in one file:

  <plugin>
    <groupId>com.internetitem</groupId>
    <artifactId>write-properties-file-maven-plugin</artifactId>
    <version>1.0.1</version>
    <executions>
      <execution>
        <id>one</id>
        <phase>compile</phase>
        <goals>
            <goal>write-properties-file</goal>
        </goals>
        <configuration>
          <filename>test.properties</filename>
          <properties>
            <property>
              <name>one</name>
              <value>1</value>
            </property>
            <property>
              <name>artifactId</name>
              <value>My Artifact ID is ${project.artifactId}</value>
            </property>
          </properties>
        </configuration>
      </execution>
    </executions>
  </plugin>
Gambotic
  • 824
  • 8
  • 19
2

you can use the maven-antrun-plugin and regex expressions to input the version into the file. PS: version.txt file content is any string ex:version.

 ...
    <plugins>
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
         <version>1.4</version>
         <executions>
          <execution>
            <phase>process-resources</phase>
            <goals>
             <goal>run</goal>
            </goals>
            <configuration>
               <target>
                 <replaceregexp file="resources/version.txt" match=".*" replace="${project.version}" byline="true" />
              </target>
            </configuration>
          </execution>
        </executions>
      </plugin>
   </plugins>
chashikajw
  • 592
  • 5
  • 18
2

Just use the maven-help-plugin

<build>
  <plugins>
     <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-help-plugin</artifactId>
        <version>3.2.0</version>
        <executions>
            <execution>
                <id>generate-version-file</id>
                <phase>prepare-package</phase>
                <goals>
                    <goal>evaluate</goal>
                </goals>
                <configuration>
                    <expression>project.version</expression>
                    <output>${project.build.directory}/version.txt</output>
                </configuration>
            </execution>
        </executions>
    </plugin>

Source

thi gg
  • 1,969
  • 2
  • 22
  • 47
1

One possibility is to store all project properties to the built .jar using maven-properties-plugin.
Then you can read these properties using standard (though not too practical) Java Properties API.

        <!-- Build properties to a file -->
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>properties-maven-plugin</artifactId>
            <version>1.0.0</version>
            <executions>
                <execution>
                    <phase>generate-resources</phase>
                    <goals> <goal>write-project-properties</goal> </goals>
                    <configuration>
                        <outputFile> ${project.build.outputDirectory}/build.properties </outputFile>
                    </configuration>
                </execution>
            </executions>
        </plugin>

Be careful with this approach as it may leak properties that are not supposed to end up published, also from settings.xml.

Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
1

Adding below plugin in pom.xml worked for me. This is a combination of above answers only:-

<project>
  [...]
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>3.0.0</version>
        <executions>
          <execution>
            <phase>package</phase>
            <configuration>
              <target><echo file="version.txt">${project.version}</echo> </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  [...]
AlphaBetaGamma
  • 1,910
  • 16
  • 21
0

To add to Sean's answer, you can move the version file to a folder location within the jar by using the targetpath parameter within resource. The following code creates a folder called 'resources' within the jar and the text file (version.number) is found in that folder.

<resource>
    <directory>resources</directory>
    <targetPath>resources</targetPath>
    <filtering>true</filtering>
    <includes>
        <include>version.number</include>
    </includes>
</resource>
<resource>
    <directory>resources</directory>
    <filtering>false</filtering>
    <excludes>
        <exclude>version.number</exclude>
    </excludes>
</resource>
learmo
  • 1
  • 1
0

Create version.txt with following content in /src/version.txt

Build version: ${project.version}

Use assembly plugin and add step in dist.xml with filtering=true

<files>
    <file>
        <source>${basedir}/src/version.txt</source>
        <outputDirectory>${root.dist.folder}</outputDirectory>
        <filtered>true</filtered>
   </file>
</files>
Roobal Jindal
  • 214
  • 2
  • 13