Scripting Maven Deployments with Ruby

Recently I had to import a number of legacy projects into a Maven-type structure. I knocked up the following script to make the task easier for repeated applications. Basically what it does is the following:

  • Tried to parse the filenames of jars in the current directory;
  • Does a Nexus search to see if it can locate the artifact;
  • If it finds the artifact in Nexus, it can use the GAV parameters for that artifact, otherwise, it generates a GAV stanza for inclusion in the POM.
  • For each artifact that could not be located in Nexus, it generates a mvn deploy:deploy-file command to upload the dependency

Error handling is non-existent in this file, so caveat emptor!

#!/usr/bin/ruby

require 'net/http'
require 'rexml/document'
include REXML

# Nexus server
host="nexus-server"
port=8080

# Could also use GAV parameters in query string
url="http://#{host}:#{port}/nexus/service/local/data_index?q="

print "<dependencies>","\n"

# Open file for dependencies entries
depsFile=File.open("dependencies.xml",'w')
depsFile.write("</dependencies><dependencies>\n")

# Track unprocessed jars
errors=[]

# Jars to be uploaded to Nexus
uploads=[]

for jarFile in Dir.glob("*.jar")
    if jarFile =~ /([A-Za-z\-\.0-9]+?)(?:\-)?(\d+(?:\.\d+)*)?.jar/
        artifact=$1
        version=$2

        if artifact =~ /(\S+)\.(.*)/
            group=$1
            artifact=$2
        else
            group=artifact.gsub("\-", "\.")
        end    

        if version.nil?
            version="1.0"
        end

        # Create a default stanza template
        stanza = "<dependency>\n"
        stanza < < '  <groupId>#{group}\n'
        stanza < < '  <artifactId>#{artifact}<artifactid>\n'
        stanza < < '  <version>#{version}\n'
        stanza < < "  <packaging>jar\n"
        stanza < < "</dependency>\n\n"

        puts "Searching Nexus for #{artifact}"
        query="#{url}#{artifact}"

        resp = Net::HTTP.get_response(URI.parse(query))
        respDoc = REXML::Document.new(resp.body)
        XPath.each(respDoc, "//search-results/totalCount") do |count|
            matches = Integer(count.text)
            if matches > 0

                idx=1; artifactMap = {}
                XPath.each( count, "//data/artifact") do |ar|
                    artifactId=ar.elements["artifactId"].text
                    artifactGroupId=ar.elements["groupId"].text
                    artifactVersion=ar.elements["version"].text
                    artifactRepo=ar.elements["repoId"].text
                    puts "#{idx}. #{artifactGroupId}:#{artifactId}:#{artifactVersion} (repo:#{artifactRepo})"
                    artifactMap[idx]={"group",artifactGroupId, "artifact",artifactId, "version",artifactVersion}
                    idx+=1
                end

                puts "Found #{matches} possible match(es) for #{group}:#{artifact}:#{version}"
                puts "Enter artifact number, or enter to use generated artifact:"
                num=gets.chomp

                if !num.empty?
                    num=Integer(num)
                    entry=artifactMap[num]
                    group=entry["group"];artifact=entry["artifact"];version=entry["version"]
                else
                    uploads.push( {"artifact",artifact,"group",group,"version",version,"file",jarFile} )
                end

            else
                uploads.push( {"artifact",artifact,"group",group,"version",version,"file",jarFile} )
            end
            stanza = eval('"' + stanza + '"')
            depsFile.write(stanza)
        end
    else
        errors < < jarFile
    end
end

depsFile.write("</dependencies>\n")
depsFile.close

puts "Finished processing.\nCheck the file dependencies.xml for generated XML"

# Generate the upload script
File.open("upload_deps.sh", "w") { |f|
    f.puts "#!/bin/bash\n\n"
    uploads.each do |dep|
        # Send the file to the correct repo
        repo="http://#{host}:#{port}/nexus/content/repositories/"
        if dep["version"] =~ /(.*)SNAPSHOT/
            repo < < "snapshots"
        else
            repo << "releases"
        end

        line="mvn deploy:deploy-file -Dfile=#{dep["file"]} -DartifactId=#{dep["artifact"]} -DgroupId=#{dep["group"]} -Dversion=#{dep["version"]} -Dpackaging=jar -DrepositoryId=nexus -DgeneratePom=true -Durl=#{repo}"

        f.puts "echo \"Uploading #{dep["file"]}...\""
        f.puts line
    end
}

puts <<HERE
The file upload_deps.sh contains Maven commands to upload dependencies not currently in Nexus.

This assumes that you have a stanza in $MVN_HOME/conf/settings.xml as follows:

<server>
    <id>nexus</id>
    <username>$USERNAME</username>
    <password>$PASSWORD</password>

Where the appropriate Nexus deployment credentials are in <username> and <password>.
HERE

if !errors.empty?
    puts "The following jar files could not be processed and need to be manually defined:"
    errors.each do |error|
        puts error
    end
end

Comments are closed.