One challenge of the maven way is how to sensibly version dependencies in a large, but constantly changing, project.
In the ideal maven world, each module is a highly defined project, with a highly structured team and releases only occur once the contract has been fully defined, tested, and whatever else enterprise teams do. In many realities, projects are simply logical constructs that don't have strict contracts between the other module. The production environment is a mix of highly stable and rigorously tested products and experimental, pushed-constantly projects.
In this environment, automatic versioning of dependencies is both unnecessary and costly, and the dependencies need to be frozen at branch time of the stable, top-level projects. The other requirement is that the changes to the poms need to be minimal, so that any merge conflicts can be easily resolved.
The approach I've used, combining a few different ideas from the internet, is to remove any version numbers referencing internal projects from child modules. The versions are then included as properties in the parent project.
Freezing the set of dependencies for the module tree is then a three part process of: - Change the version number in the parent pom - Change the version properties of the parent pom - Change the parent version number in the child poms
To illustrate how this works, consider a project that has com.tejusparikh.web
with a parent pom com.tejusparikh
.
<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/maven-v4_0_0.xsd">
<modelversion>4.0.0</modelversion>
<groupid>com.tejusparikh</groupid>
<artifactid>tejusparikh</artifactid>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>web</module>
</modules>
<properties>
<tejusparikh-version>1.0-SNAPSHOT</tejusparikh-version>
</properties>
</project>
<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/maven-v4_0_0.xsd">
<parent>
<groupid>com.tejusparikh</groupid>
<artifactid>tejusparikh</artifactid>
<version>1.0-SNAPSHOT</version>
</parent>
<modelversion>4.0.0</modelversion>
<groupid>com.tejusparikh.web</groupid>
<artifactid>tejusparikh-web</artifactid>
<packaging>war</packaging>
<version>${tejusparikh-version}</version>
</project>
If you wanted to freeze the poms at 1.0.RELEASE
then the poms would look like the following:
<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/maven-v4_0_0.xsd">
<modelversion>4.0.0</modelversion>
<groupid>com.tejusparikh</groupid>
<artifactid>tejusparikh</artifactid>
<packaging>pom</packaging>
<version>1.0.RELEASE</version>
<modules>
<module>web</module>
</modules>
<properties>
<tejusparikh-version>1.0.RELEASE</tejusparikh-version>
</properties>
</project>
<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/maven-v4_0_0.xsd">
<parent>
<groupid>com.tejusparikh</groupid>
<artifactid>tejusparikh</artifactid>
<version>1.0.RELEASE</version>
</parent>
<modelversion>4.0.0</modelversion>
<groupid>com.tejusparikh.web</groupid>
<artifactid>tejusparikh-web</artifactid>
<packaging>war</packaging>
<version>${tejusparikh-version}</version>
</project>
The point is that every child pom would have a property as it's version number, making it easy to manage an arbitrary number of children.
Of course, you don't want to have to update everything manually. The only method I've found to effectively use maven is to wrap it all in rake
. Therefore, I've created a rake task to recurse the directories and change the version numbers.
NAMESPACE_MAP = {'ns' => 'http://maven.apache.org/POM/4.0.0'}
require 'nokogiri'
require 'find'
namespace :mvn do
desc "[path, version] upgrade this version and all of it's children to the version specified as the second character"
task :version, :path, :version_str do |t, args|
base_path = args[:path]
version = args[:version_str]
puts "Updating all poms in #{base_path} to parent version #{version}"
Find.find(base_path) do |path|
if FileTest.directory?(path)
if File.basename(path)[0] == ?.
Find.prune # Don't look any further into this directory.
else
next
end
else
basename = File.basename(path)
if(basename == 'pom.xml')
handle_pom(version, path)
end
end
end
end
def handle_pom(version, path)
doc = Nokogiri::XML(File.open(path)) {|x| x.noblanks }
packaging = doc.xpath('/ns:project/ns:packaging', NAMESPACE_MAP).first.text().strip()
if(packaging == "pom")
puts "Updating parent pom (#{path}) "
version_node = doc.xpath('/ns:project/ns:version', NAMESPACE_MAP).first
else
puts "Updating child pom (#{path}) "
version_node = doc.xpath('/ns:project/ns:parent/ns:version', NAMESPACE_MAP).first
end
unless version_node.nil?
version_node.content = version
File.open(path,'w') do |f|
doc.write_xml_to f, :indent_text => ' ', :indent => 4
end
end
end
end
Freezing all of your poms is as simple as calling rake mvn:version[/path/to/parent/dir,VERSION]
.