Search code examples
xmlbashxpathsedxmlstarlet

Replace a dependency version in pom.xml programmatically?


Assuming I have a pom.xml containing a parent dependency like this:

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.example</groupId>
        <artifactId>some-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
</project>

Is there a way I can replace the 1.0.0-SNAPSHOT version programmatically, maybe via a script?

My use case:

  • I have lots of pom.xml files that I need to change the parent version of
  • I'm using git-xargs to make changes across multiple repos, so I can apply a bash script to each pom.xml to change it.

For example, I can do the following, but this will update all occurrences of 1.0.0-SNAPSHOT in the pom.xml, and I want to limit it to just the some-parent artifact that has a version 1.0.0-SNAPSHOT:

git-xargs \
  --branch-name test-branch \
  --github-org <your-github-org> \
  --commit-message "Update pom.xml" \
  sed -i 's/1.0.0-SNAPSHOT/2.0.1/g' pom.xml

As per the git-xargs docs, I can use any type of script to process the pom.xml, bash, python, ruby etc: https://github.com/gruntwork-io/git-xargs#how-to-supply-commands-or-scripts-to-run

UPDATE:

The following xmlstarlet approach works up to a point:

if [[ $(xmlstarlet sel -N my=http://maven.apache.org/POM/4.0.0 -t -v '//my:project/my:parent/my:artifactId' pom.xml) == "some-parent" && $(xmlstarlet sel -N my=http://maven.apache.org/POM/4.0.0 -t -v '//my:project/my:parent/my:version' pom.xml) == "1.0.0-SNAPSHOT" ]]; then xmlstarlet edit -L -N my=http://maven.apache.org/POM/4.0.0 --update '//my:project/my:parent/my:version' --value '2.0.6' pom.xml; fi

xmlstarlet is correctly updating the xml element I want, but its also reordering the xsi:schemaLocation, xmlns and xmlns:xsi at the top of my file.

It's updating it from:

<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

to:

<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/xsd/maven-4.0.0.xsd">

which is no good for me.


Solution

  • You should avoid working with xml as plain text files, but if you have no other options you may give it a try. sed ranges (/starting-regex/,/ending-regex/{commands}) allows to restrict your replacement within specific context. I.e. this oneliner:

    sed -e '/<parent/,/\/parent/{/artifactId>some-parent/,/parent/{s/1.0.0-SNAPSHOT/2.0.0-SNAPSHOT/g}}'

    will replace only version strings, which are between <parent and /parent strings, and between some-parent and parent lines, thus should only match artifacts some-parent. Obviously, there are multiple cases when it will fail, but for simple files this could work. Your ranges may be further nested.