Skip to content

Instantly share code, notes, and snippets.

@eddieantonio
Created July 22, 2013 18:43
Show Gist options
  • Save eddieantonio/6056410 to your computer and use it in GitHub Desktop.
Save eddieantonio/6056410 to your computer and use it in GitHub Desktop.
A useless Python script I used to collect JARs and WARs from a directory of Maven projects.
#!/usr/bin/env python
"""
Build all Maven files and dump all of the jars into a directory.
"""
import os
import sys
import re
import glob
import shutil
import xml.etree.ElementTree as ET
from contextlib import contextmanager
RELEASE_DIR = os.path.join(os.getcwd(), 'release')
# ANSI colours
BLACK = 0
RED = 1
GREEN = 2
YELLOW = 3
BLUE = 4
MAGENTA = 5
CYAN = 6
WHITE = 7
RESET = '\033[0m'
@contextmanager
def ansi_color(foreground=None, background=None, bold=False, stream=sys.stdout):
"""
Surrounds write to `stream` with the given ANSI colour.
Maybe I'm going crazy with context managers...
"""
codes = []
if foreground:
codes.append(foreground | int('30', base=8))
if background:
codes.append(background | int('40', base=8))
if bold:
codes.append(1)
# Create the inner... part
inner = ';'.join('%o' % num for num in codes)
escape = '\033[%sm' % inner
stream.write(escape)
yield
stream.write(RESET)
stream.flush()
def find_maven_projects(top_level='.'):
"""
Generates a tuple of (path, pom) for each Maven project found in the given
top level. Defaults to the current working directory.
`pom` is parsed into an ElementTree object.
"""
# Get subdirectories. Pretend I have pattern matching.
_, subdirectories, _ = next(os.walk(top_level))
for directory in subdirectories:
# Skip if no pom is found
if 'pom.xml' not in os.listdir(directory):
continue
# Else, parse it and yield it.
pom_filename = os.path.join(directory, 'pom.xml')
pom = ET.parse(pom_filename)
yield directory, pom
def war_handler(project_dir, settings, pom):
"""
Handles WAR packaging.
"""
# TODO
raise NotImplementedError('gotta figure this one out :/')
def jar_handler(project_dir, settings, pom):
"""
Handles JAR packaging.
"""
# Glob for the fully-qualified path of the project dir.
jar_glob = os.path.join(os.getcwd(), project_dir, 'target', '*.jar')
jars = glob.iglob(jar_glob)
for jar_path in jars:
# Get the base name of the jar, and the new path.
jar_name = os.path.basename(jar_path)
new_path = os.path.join(RELEASE_DIR, jar_name)
# Now copy it over.
shutil.copy(jar_path, new_path)
def unknown_handler(project_dir, settings, pom):
"""
Handles unknown packaging.
"""
print("Cannot package '%s'!" % project_dir)
packaging_handlers = {
'war': war_handler,
'jar': jar_handler
}
def get_namespace(element):
"""
Gets the XML namespace of a given object. Returns None if no namespace is
defined.
"""
# Extract the name from the root tag.
root_name = element.tag
match = re.match(r'^{([^}]+)}', root_name)
# Get the XML namespace, or else get None.
return match.group(1) if match else None
def fix_namespaces(tree):
"""
Inserts the proper namespace prefix for the sequence 'n:' in an XPath
query.
Returns the partially applied function that does the namespace fixing for
a given string.
>>> pom = ET.fromstring('<project xmlns="http://maven.apache.org/POM/4.0.0"></project>')
>>> fix_namespaces(pom)('./n:packaging')
'./{http://maven.apache.org/POM/4.0.0}packaging'
"""
namespace = get_namespace(tree)
# Render and compile the replacement and the regex.
replacement = '{%s}' % namespace if namespace is not None else ''
path_pattern = re.compile(r'\bn:')
def fixer(path):
return path_pattern.sub(replacement, path)
return fixer
def get_packaging_handler(pom):
"""
Returns a function for the given pom ElementTree that, when called will
place the function into the correct directory.
"""
# Get the packaging directive.
query = fix_namespaces(pom.getroot())(r'./n:packaging')
packaging = pom.find(query)
# Assume 'jar' if there is no packaging directive.
packaging_name = packaging.text if packaging is not None else 'jar'
# Return the proper handler, or else use the unknown handler.
return packaging_handlers.get(packaging_name, unknown_handler)
@contextmanager
def cd(next_dir):
"""
Changes the current working directory to `next_dir` for the given context.
The current working directory is reverted when the context is left.
>>> with cd('/tmp'), open('hello', 'w') as outfile:
... outfile.write('hi!')
>>> with open('/tmp/hello') as infile:
... print(infile.read())
hi!
"""
original_dir = os.getcwd()
os.chdir(next_dir)
yield
os.chdir(original_dir)
def main(top_level='.'):
# Get a list of all maven projects.
projects = find_maven_projects(top_level)
for name, pom in projects:
with ansi_color(RED, bold=True):
print("Project: %s" % name)
# Run Maven in the project directory.
with cd(name):
os.system('mvn package')
# Now, get a handler based on the packagin
handle = get_packaging_handler(pom)
# And now handle it!
handle(name, {}, pom)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment