15

I'm maintaining an opensource project on github that contains README.md file with install instructions containing the current version of the project 1.0.0-SNAPSHOT.

When releasing a new version to maven central, is it possible to automatically update this version number contained in README.md ?

Releases are performed with maven-release-plugin, versions in pom.xml are well updated, but I can't find anything in the docs to update this external file properly.

Example:

README.md file currently at 1.0.0-SNAPSHOT. It lies outside of maven sources/resources, but it's managed on git. on mvn release:prepare, it should be updated to 1.0.0, and maven should then commit/push the change before tagging the new release. Then, on mvn release:perform it should go to the next development version 1.0.1-SNAPSHOT.

(Link to the project)

Toilal
  • 3,301
  • 1
  • 24
  • 32
  • Like this http://stackoverflow.com/questions/3532135/using-maven-to-output-the-version-number-to-a-text-file ? – Marged Jan 06 '16 at 09:15
  • @Marged no, don't think it's the same. In my case, ```README.md``` file is outside of maven sources, but managed on git. So I want release plugin to update it with the new version and then commit/push the change before tagging the new release on git during ```mvn release:prepare```. On ```mvn release:perform```, it should then go to the next development version, ```1.0.1-SNAPSHOT``` for example. – Toilal Jan 06 '16 at 09:19
  • That's tricky. `README.md` is an external file that should not be inside the final JAR. This means you need to replace that token before the release, commit the changes, push it and rollback the file to the token. Since there are two steps `prepare` and `perform`, I'm not even sure you can do it effectively. What if you do `prepare` and `clean` after? Then the `README` will be changed, unless you roll it back manually. – Tunaki Jan 06 '16 at 09:24
  • @Tunaki Yes this is the main problem I think. Also, ```release:perform``` requires all changes to be pushed on git, so making a change in this phase to ```README.md``` file would break things ... unless changes are immediately pushed on git. If nothing exists now, maybe I could try to write a maven plugin to perform this ? Using no token, but a simple string replacement of old-version with new-version – Toilal Jan 06 '16 at 09:30
  • Maybe something can be done with this : http://maven.apache.org/maven-release/maven-release-plugin/examples/run-goals-before-commit.html. But it still requires some plugin to perform the version replacement. – Toilal Jan 06 '16 at 09:43
  • 1
    I think there's a conceptual problem here. The version replacement is not a problem: you can execute a goal before a release and use `maven-replacer-plugin` to replace the token. But the problem is ensuring there are no side-effects in case things go wrong. – Tunaki Jan 06 '16 at 09:46
  • the `unleash-maven-plugin` is meant to be an alternative to the `maven-release-plugin` and allow for something just like this. – jae Sep 03 '16 at 19:28

4 Answers4

9

You can use the maven-resources-plugin for this like mentioned in the comments.

I didn't try it but the configuration should look something like this:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-resources-plugin</artifactId>
  <version>3.0.1</version>
  <executions>
    <execution>
      <id>readme-md</id>
      <phase>process-resources</phase>
      <goals>
        <goal>copy-resources</goal>
      </goals>
      <configuration>
        <outputDirectory>${project.basedir}</outputDirectory>
        <resources>                                        
          <resource>
            <directory>${project.basedir}</directory>
            <includes>
              <include>README.md</include>
            </includes>
            <filtering>true</filtering>
          </resource>
        </resources>
        <encoding>UTF-8</encoding>
      </configuration>            
    </execution>
  </executions>
</plugin>

And in your README.md where you want the version you put the placeholder ${project.version}.

The two features which where combined here are copy-resources and filtering.

We tell the plugin to copy resources from the directory ${project.basedir}, which maven resolves to the root directory, again to the root directory but only include files matching README.md.

The filtering option replaces all placeholders with variables which can be defined as system properties, project properties, filter resources defined in the pom.xml or on command line. In this case we use the project property version.

Though the comments are right mentioning eventually inconsistency in case the release went wrong. You could overcome this by explicitly calling mvn resources:resources after your mvn release:perform was successful. I hope this helps.

Marvin Richter
  • 591
  • 6
  • 11
  • Yes, including README.md in resources seems to be a really smart idea. I didn't know it was possible. Only drawback is that this file will be included in the packaged jar file, but I don't think it's an issue ! And it's surely possible to exclude it in jar plugin configuration. Thank you ! – Toilal Nov 08 '16 at 12:58
  • 1
    I personally don't think it is a drawback to include it in the binary. I even think it's a big plus. Your README.md is part of the documentation and there should be a 1:1 relationship between a release and it's documentation. Having tools nicely presenting them in the repository is just a bonus but shouldn't be the only possible way to read it. – Marvin Richter Nov 08 '16 at 13:06
  • 1
    Glad I could help :) – Marvin Richter Nov 08 '16 at 13:19
  • 4
    Problem is that it will work for 1st release only. For next release, {$project.version} placeholders are replaced by effective project version. I need to find a way to restore them back after the release ... – Toilal Nov 24 '16 at 18:33
  • 1
    Right. Didn't thought of this. Than I would recommend, putting the README.md under src/main/doc and copy it from their to the root folder – Marvin Richter Nov 24 '16 at 18:58
  • I think i'll use [completionGoals](http://maven.apache.org/maven-release/maven-release-plugin/prepare-mojo.html#completionGoals) of release:prepare goal along with maven-replacer-plugin – Toilal Nov 24 '16 at 19:25
  • Sure that is totally viable option. Maybe even the better one because it aligns semantically with what you want to achieve. Makes it easier to understand for others. I never really liked the release plugin, though. – Marvin Richter Nov 24 '16 at 19:42
  • But which alternatives available to release on maven central or private nexus ? – Toilal Nov 24 '16 at 19:43
1

You can try writing a bash script that builds your project and also modifies README.md file. Something like:

#!/bin/sh
VERSION=$1
mvn clean package
#modify the string manipulation command accordingly
sed -e "s/{VERSION}/${VERSION}/" ./README.md-tpl > ./README.md 

when running script, you can pass version number as first argument. You can use full path to your default README.md-tpl file.

gmode
  • 3,601
  • 4
  • 31
  • 39
  • It should run on windows too. I want to keep this "maven native". – Toilal Jan 06 '16 at 09:31
  • You can start using Docker, which abstracts all the environment issues and solves OS related problems – gmode Jan 06 '16 at 09:36
  • I already use Docker since about 1 year, thanks it's great, but it won't solve my initial problem :(. Build should really run as a standard maven build. – Toilal Jan 06 '16 at 09:39
1

Building onto Marvins answer,
you can keep your README.md in your usual place and include just a snippet of text.

The trick is that although Markdown does not allow to include text, it allows to include an svg-image and an svg-image can contain text that can be selected and copied in all browsers:

<svg xmlns="http://www.w3.org/2000/svg"
     xml:lang="en-GB" width="auto" height="7em">
    <style>
        svg {
            font-family: monospace;
            font-size: medium;
        }
    </style>

    <text x="0" y="1em">
        <tspan x="1em" dy="1em">&lt;dependency&gt;</tspan>
        <tspan x="3em" dy="1em">&lt;groupId&gt;${project.groupId}&lt;/groupId&gt;</tspan>
        <tspan x="3em" dy="1em">&lt;artifactId&gt;${project.artifactId}&lt;/artifactId&gt;</tspan>
        <tspan x="3em" dy="1em">&lt;version&gt;${project.version}&lt;/version&gt;</tspan>
        <tspan x="1em" dy="1em">&lt;/dependency&gt;</tspan>
    </text>
    
</svg>

This will render as:

<dependency>
  <groupId>${project.groupId}</groupId>
  <artifactId>${project.artifactId}</artifactId>
  <version>${project.version}</version>
</dependency>

(It will become a one-liner on paste, but that's okay. pom.xml are usually auto-formatted anyway)

You put that image into a directory called "templates" and a copy into a "docimages"-directory.

Now you can add your image to the README.md using a normal image-reference:
![mvn dependency](docimages/version.svg)
Note you're referencing the one in your "docimages" directory, not the one in your "templates" dir.

Now you just need a job in your pom.xml that copies and filters the image's source code:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-resources-plugin</artifactId>
  <version>3.0.1</version>
  <executions>
    <execution>
      <id>readme-md</id>
      <phase>process-resources</phase>
      <goals>
        <goal>copy-resources</goal>
      </goals>
      <configuration>
        <outputDirectory>${project.basedir}/docimages</outputDirectory>
        <resources>                                        
          <resource>
            <directory>${project.basedir/templates}</directory>
            <includes>
              <include>version.svg</include>
            </includes>
            <filtering>true</filtering>
          </resource>
        </resources>
        <encoding>UTF-8</encoding>
      </configuration>            
    </execution>
  </executions>
</plugin>

"What's the benefit over filtering all of the readme?" You might ask.
Simple: A README.md can be large and can contain all kinds of variables as part of the documentation that you don't want to substitute. This will only substitute the variables in the SVG-file.

Brixomatic
  • 381
  • 4
  • 16
  • clever. I just wish when you embed the svg image to a github markdown file, you didn't have to click it twice to actually select the text. – Matthew Madson Jun 30 '21 at 07:42
  • 1
    That's something that unfortunately every markdown renderer does differently. I wished there was an official way to include text though, I generally do not like dirty hacks. – Brixomatic Jul 01 '21 at 11:42
1

I have a combination of a few suggestions.

First, I substitued ${project.version} for the version in my README.md and moved it to the src/main/doc folder as Marvin suggested.

Then, I added an execution to the maven-resources-plugin in my build, like this:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.0.1</version>
        <executions>
            <execution>
                <id>readme</id>
                <phase>process-resources</phase>
                <goals>
                    <goal>copy-resources</goal>
                </goals>
                <configuration>
                    <outputDirectory>${project.basedir}</outputDirectory>
                    <resources>
                        <resource>
                            <directory>src/main/doc</directory>
                            <includes>
                                <include>README.md</include>
                            </includes>
                            <filtering>true</filtering>
                        </resource>
                    </resources>
                    <encoding>UTF-8</encoding>
                </configuration>
            </execution>
        </executions>
    </plugin>

Now, every time the plugin runs, it substitutes the project version in README.md and copies it to ${project.basedir}.

Finally, in the maven-release-plugin plugin, I added a few goals to run the above plugin, then add the newly updated README.md to the index before the commit:

   <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-release-plugin</artifactId>
        <version>3.0.0-M4</version>
        <configuration>
            <preparationGoals>resources:copy-resources@readme scm:add -Dincludes=README.md</preparationGoals>
            <completionGoals>resources:copy-resources@readme scm:add -Dincludes=README.md</completionGoals>
        </configuration>
    </plugin>

Now each time the release plugin commits the pom.xml, it will commit the README.md as well.

ocarlsen
  • 1,312
  • 2
  • 16
  • 20