Skip to content

Instantly share code, notes, and snippets.

@cb372
Last active November 1, 2019 10:07
Show Gist options
  • Save cb372/6681098 to your computer and use it in GitHub Desktop.
Save cb372/6681098 to your computer and use it in GitHub Desktop.
Version-bump script for Maven projects. Reads pom.xml, parses the version, increments it and either updates pom.xml or writes an updated pom to stdout.
#! /usr/bin/env python
#
# coding: utf-8
"""
Version-bump script for Maven projects.
Reads pom.xml, parses the version, increments it and writes an updated pom to stdout.
Usage:
pom-vbump.py [-i] [-v <new version number>] [path to pom.xml]
Options:
-n Dry run: just print the new version number that would be written to pom.xml
-i Edit pom.xml in place, instead of writing result to stdout
-v specify a version number, e.g. "1.23"
If the next version number (v_n) is not specified, it will be guestimated from
the current version (v_c) using the following rules:
- if v_c is of the form 'a-SNAPSHOT',
remove the '-SNAPSHOT' part, so v_n = a
- if v_c is of the form 'a.b.c...z' where z is an integer,
increment z and add '-SNAPSHOT', so v_n = a.b.c...(z+1)-SNAPSHOT
- otherwise give up and throw an error
If pom.xml file is not specified, the script will look in the current working directory.
Tested with Python 2.7.5 and 3.3.2.
Requires lxml.
"""
import sys
import getopt
import os.path
from lxml import etree as ET
class InvalidVersion(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return "Invalid version: " + self.msg
def main(args):
next_version = None
in_place = False
dry_run = False
pom_xml = './pom.xml'
try:
opts, args = getopt.getopt(args, 'nv:ih', ['dryrun', 'version', 'inplace', 'help'])
except getopt.GetoptError:
usage()
return False
for opt, value in opts:
if opt in ('-h', '--help'):
usage()
return False
elif opt in ('-v', '--version'):
next_version = value
elif opt in ('-i', '--inplace'):
in_place = True
elif opt in ('-n', '--dryrun'):
dry_run = True
else:
usage()
return False
if len(args) > 0:
pom_xml = args[0]
if not os.path.isfile(pom_xml):
log("ERROR: Could not find pom.xml file: %s" %pom_xml)
usage()
return False
try:
bump(pom_xml, next_version, in_place, dry_run)
return True
except InvalidVersion as e:
log(e)
return False
def usage():
print(__doc__)
def bump(pom_xml, next_version, in_place, dry_run):
log("Parsing %s..." % pom_xml)
parser = ET.XMLParser(remove_comments=False)
xml = ET.parse(pom_xml, parser=parser)
# Find the project's current version
version_tag = xml.find("./{*}version")
if version_tag == None:
raise InvalidVersion("pom.xml does not appear to have a <version> tag")
current_version = version_tag.text
log("Current version is %s" % current_version)
# Calculate the next version, if not specified
if not next_version:
next_version = increment_version(current_version)
# If dry run, just print the next version and exit
if dry_run:
print next_version
else:
log("Incrementing version to %s" % next_version)
# Update the XML
version_tag.text = next_version
if in_place:
# Write back to pom.xml
write_xml_to_file(xml, pom_xml)
else:
# Print result to stdout
print_xml(xml)
def increment_version(current_version):
# If it's a snapshot version, convert it to a release version
if current_version.endswith('-SNAPSHOT'):
return current_version[:-9]
parts = current_version.split(".")
last_part = parts[len(parts) - 1]
try:
# Add one to the final part of the version string
incremented_last_part = str(int(last_part) + 1)
except TypeError:
raise InvalidVersion("Unsuppported version format [%s]" % current_version)
# Try to maintain the zero padding of the old version, if any
incremented_last_part = incremented_last_part.zfill(len(last_part))
# Make it a snapshot version
incremented_last_part = incremented_last_part + '-SNAPSHOT'
return ".".join(parts[:-1] + [incremented_last_part])
def write_xml_to_file(xml, output_file):
with open(output_file, 'wb') as f:
f.write(ET.tostring(xml, encoding="utf-8", xml_declaration=True))
def print_xml(xml):
result = ET.tostring(xml, encoding="utf-8", xml_declaration=True)
if sys.hexversion >= 0x03000000:
# Python 3.x
sys.stdout.buffer.write(result)
else:
# Python 2.x
print(result)
def log(msg):
sys.stderr.write(str(msg) + '\n')
if __name__ == '__main__':
sys.exit(not main(sys.argv[1:]))
Copy link

ghost commented May 12, 2014

How about using maven?

For example:

mvn versions:set -DnewVersion=1.2.3-SNAPSHOT

@rkosegi
Copy link

rkosegi commented Dec 28, 2016

Your python script is result of not understanding tools you are using (maven).

This is correct way how to bump version of maven project using maven:

mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}.\${parsedVersion.nextIncrementalVersion} versions:commit

@replay111
Copy link

replay111 commented Nov 1, 2019

I have in my pom this section:

<groupId>com.sample.app</groupId>
<artifactId>simpleapp</artifactId>
<version>1.0.9-${buildNumber}-SNAPSHOT</version>
<packaging>war</packaging>

Is there any nice and "maven way" to keep this variable name ${buildNumber} in that place in pom as this is now?
I mean - when I am performing

mvn deploy:prepare deploy:release  -DbuildNumber=${BUILD_NUMBER}

this version section in pom.xml is updated to (when BUILD_NUMBER eq: 12):

<version>1.0.9-12-SNAPSHOT</version>

which almost is ok but this is also commited to repository. I like the fact that this tag: 1.0.9-12 in git repo is created, but I prefer to keep my original format of version in pom.xml file:

<version>1.0.9-${buildNumber}-SNAPSHOT</version>

This is because this stupid approach I have in my company to add to artefact version also build number from CI tool :(

Can someone give me some hint how to handle this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment