Added recursive option
Now you can organize your yaml files into subdirectories and apply them all just passing --recursive option or adding the proper configuration file option. In adding support for multiple paths the Builder.load_files method was reworked to be backward compatible with individual paths or file-like objects, while also now handling being passed a list of paths or file-like objects to be parsed. Change-Id: I126751e347622716c592c6ed1a57b8acb7bf79a4
This commit is contained in:
parent
df37b9b7c7
commit
a70126cd94
|
@ -70,6 +70,10 @@ job_builder section
|
|||
job builder will search for any files specified by the custom application
|
||||
yaml tags 'include', 'include-raw' and 'include-raw-escaped'.
|
||||
|
||||
**recursive**
|
||||
(Optional) If set to True, jenkins job builder will search for job
|
||||
definition files recursively
|
||||
|
||||
jenkins section
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
ignore_cache=True
|
||||
keep_descriptions=False
|
||||
include_path=.:scripts:~/git/
|
||||
recursive=False
|
||||
|
||||
[jenkins]
|
||||
user=jenkins
|
||||
|
|
|
@ -559,20 +559,37 @@ class Builder(object):
|
|||
def load_files(self, fn):
|
||||
self.parser = YamlParser(self.global_config)
|
||||
|
||||
if hasattr(fn, 'read'):
|
||||
self.parser.parse_fp(fn)
|
||||
return
|
||||
# handle deprecated behavior
|
||||
if not hasattr(fn, '__iter__'):
|
||||
logger.warning(
|
||||
'Passing single elements for the `fn` argument in '
|
||||
'Builder.load_files is deprecated. Please update your code '
|
||||
'to use a list as support for automatic conversion will be '
|
||||
'removed in a future version.')
|
||||
fn = [fn]
|
||||
|
||||
if os.path.isdir(fn):
|
||||
files_to_process = [os.path.join(fn, f)
|
||||
for f in os.listdir(fn)
|
||||
if (f.endswith('.yml') or f.endswith('.yaml'))]
|
||||
else:
|
||||
files_to_process = [fn]
|
||||
files_to_process = []
|
||||
for path in fn:
|
||||
if os.path.isdir(path):
|
||||
files_to_process.extend([os.path.join(path, f)
|
||||
for f in os.listdir(path)
|
||||
if (f.endswith('.yml')
|
||||
or f.endswith('.yaml'))])
|
||||
else:
|
||||
files_to_process.append(path)
|
||||
|
||||
for in_file in files_to_process:
|
||||
logger.debug("Parsing YAML file {0}".format(in_file))
|
||||
self.parser.parse(in_file)
|
||||
# use of ask-for-permissions instead of ask-for-forgiveness
|
||||
# performs better when low use cases.
|
||||
if hasattr(in_file, 'name'):
|
||||
fname = in_file.name
|
||||
else:
|
||||
fname = in_file
|
||||
logger.debug("Parsing YAML file {0}".format(fname))
|
||||
if hasattr(in_file, 'read'):
|
||||
self.parser.parse_fp(in_file)
|
||||
else:
|
||||
self.parser.parse(in_file)
|
||||
|
||||
def delete_old_managed(self, keep):
|
||||
jobs = self.jenkins.get_jobs()
|
||||
|
|
|
@ -31,6 +31,7 @@ DEFAULT_CONF = """
|
|||
[job_builder]
|
||||
keep_descriptions=False
|
||||
ignore_cache=False
|
||||
recursive=False
|
||||
|
||||
[jenkins]
|
||||
url=http://localhost:8080/
|
||||
|
@ -45,18 +46,32 @@ def confirm(question):
|
|||
sys.exit('Aborted')
|
||||
|
||||
|
||||
def recurse_path(root):
|
||||
basepath = os.path.realpath(root)
|
||||
pathlist = [basepath]
|
||||
|
||||
for root, dirs, files in os.walk(basepath, topdown=True):
|
||||
pathlist.extend([os.path.join(root, path) for path in dirs])
|
||||
|
||||
return pathlist
|
||||
|
||||
|
||||
def create_parser():
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
recursive_parser = argparse.ArgumentParser(add_help=False)
|
||||
recursive_parser.add_argument('-r', '--recursive', action='store_true',
|
||||
dest='recursive', default=False,
|
||||
help='look for yaml files recursively')
|
||||
subparser = parser.add_subparsers(help='update, test or delete job',
|
||||
dest='command')
|
||||
parser_update = subparser.add_parser('update')
|
||||
parser_update = subparser.add_parser('update', parents=[recursive_parser])
|
||||
parser_update.add_argument('path', help='path to YAML file or directory')
|
||||
parser_update.add_argument('names', help='name(s) of job(s)', nargs='*')
|
||||
parser_update.add_argument('--delete-old', help='delete obsolete jobs',
|
||||
action='store_true',
|
||||
dest='delete_old', default=False,)
|
||||
parser_test = subparser.add_parser('test')
|
||||
parser_test = subparser.add_parser('test', parents=[recursive_parser])
|
||||
parser_test.add_argument('path', help='path to YAML file or directory',
|
||||
nargs='?', default=sys.stdin)
|
||||
parser_test.add_argument('-o', dest='output_dir', default=sys.stdout,
|
||||
|
@ -166,13 +181,22 @@ def execute(options, config):
|
|||
ignore_cache=ignore_cache,
|
||||
flush_cache=options.flush_cache)
|
||||
|
||||
if hasattr(options, 'path') and options.path == sys.stdin:
|
||||
logger.debug("Input file is stdin")
|
||||
if options.path.isatty():
|
||||
key = 'CTRL+Z' if platform.system() == 'Windows' else 'CTRL+D'
|
||||
logger.warn(
|
||||
"Reading configuration from STDIN. Press %s to end input.",
|
||||
key)
|
||||
if hasattr(options, 'path'):
|
||||
if options.path == sys.stdin:
|
||||
logger.debug("Input file is stdin")
|
||||
if options.path.isatty():
|
||||
key = 'CTRL+Z' if platform.system() == 'Windows' else 'CTRL+D'
|
||||
logger.warn(
|
||||
"Reading configuration from STDIN. Press %s to end input.",
|
||||
key)
|
||||
|
||||
# expand or convert options.path to a list
|
||||
if (getattr(options, 'recursive', False)
|
||||
or config.getboolean('job_builder', 'recursive')) and \
|
||||
os.path.isdir(options.path):
|
||||
options.path = recurse_path(options.path)
|
||||
else:
|
||||
options.path = [options.path]
|
||||
|
||||
if options.command == 'delete':
|
||||
for job in options.name:
|
||||
|
|
|
@ -98,3 +98,37 @@ class CmdTests(testtools.TestCase):
|
|||
config = cmd.setup_config_settings(args)
|
||||
self.assertEqual(config.get('jenkins', 'url'),
|
||||
"http://test-jenkins.with.non.default.url:8080/")
|
||||
|
||||
@mock.patch('jenkins_jobs.cmd.Builder.update_job')
|
||||
@mock.patch('jenkins_jobs.cmd.os.path.isdir')
|
||||
@mock.patch('jenkins_jobs.cmd.os.walk')
|
||||
def test_recursive_path_option(self, os_walk_mock, isdir_mock,
|
||||
update_job_mock):
|
||||
"""
|
||||
Test handling of recursive path option
|
||||
"""
|
||||
|
||||
os_walk_mock.return_value = [
|
||||
('/jjb_configs', ('dir1', 'dir2', 'dir3'), ()),
|
||||
('/jjb_configs/dir1', ('bar',), ()),
|
||||
('/jjb_configs/dir2', ('baz',), ()),
|
||||
('/jjb_configs/dir3', (), ()),
|
||||
('/jjb_configs/dir1/bar', (), ()),
|
||||
('/jjb_configs/dir2/baz', (), ()),
|
||||
]
|
||||
isdir_mock.return_value = True
|
||||
paths = [path for path, _, _ in os_walk_mock.return_value]
|
||||
|
||||
args = self.parser.parse_args(['test', '-r', '/jjb_configs'])
|
||||
args.output_dir = mock.MagicMock()
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.readfp(cStringIO.StringIO(cmd.DEFAULT_CONF))
|
||||
cmd.execute(args, config) # probably better to fail here
|
||||
|
||||
update_job_mock.assert_called_with(paths, [], output=args.output_dir)
|
||||
|
||||
args = self.parser.parse_args(['test', '/jjb_configs'])
|
||||
config.set('job_builder', 'recursive', 'True')
|
||||
cmd.execute(args, config) # probably better to fail here
|
||||
|
||||
update_job_mock.assert_called_with(paths, [], output=args.output_dir)
|
||||
|
|
Loading…
Reference in New Issue