diff --git a/jenkins_jobs/cli/__init__.py b/jenkins_jobs/cli/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/jenkins_jobs/cli/entry.py b/jenkins_jobs/cli/entry.py
new file mode 100644
index 000000000..c243bbeca
--- /dev/null
+++ b/jenkins_jobs/cli/entry.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+# Copyright (C) 2015 Wayne Warren
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import argparse
+import logging
+import sys
+
+from stevedore import extension
+
+import jenkins_jobs.version
+from jenkins_jobs import cmd
+
+logging.basicConfig(level=logging.INFO)
+
+
+def __version__():
+ return "Jenkins Job Builder version: %s" % \
+ jenkins_jobs.version.version_info.version_string()
+
+
+class JenkinsJobs(object):
+ """ This is the entry point class for the `jenkins-jobs` command line tool.
+ While this class can be used programmatically by external users of the JJB
+ API, the main goal here is to abstract the `jenkins_jobs` tool in a way
+ that prevents test suites from caring overly much about various
+ implementation details--for example, tests of subcommands must not have
+ access to directly modify configuration objects, instead they must provide
+ a fixture in the form of an .ini file that provides the configuration
+ necessary for testing.
+
+ External users of the JJB API may be interested in this class as an
+ alternative to wrapping `jenkins_jobs` with a subprocess that execs it as a
+ system command; instead, python scripts may be written that pass
+ `jenkins_jobs` args directly to this class to allow programmatic setting of
+ various command line parameters.
+ """
+
+ def __init__(self, args=None):
+ if args is None:
+ args = []
+ parser = self._create_parser()
+ self._options = parser.parse_args(args)
+
+ if not self._options.command:
+ parser.error("Must specify a 'command' to be performed")
+
+ logger = logging.getLogger()
+ if (self._options.log_level is not None):
+ self._options.log_level = getattr(logging,
+ self._options.log_level.upper(),
+ logger.getEffectiveLevel())
+ logger.setLevel(self._options.log_level)
+
+ self._config_file_values = cmd.setup_config_settings(self._options)
+
+ def _create_parser(self):
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--conf',
+ dest='conf',
+ help='''configuration file''')
+ parser.add_argument(
+ '-l',
+ '--log_level',
+ dest='log_level',
+ default='info',
+ help='''log level (default: %(default)s)''')
+ parser.add_argument(
+ '--ignore-cache',
+ action='store_true',
+ dest='ignore_cache',
+ default=False,
+ help='''ignore the cache and update the jobs anyhow (that will only
+ flush the specified jobs cache)''')
+ parser.add_argument(
+ '--flush-cache',
+ action='store_true',
+ dest='flush_cache',
+ default=False,
+ help='''flush all the cache entries before updating''')
+ parser.add_argument(
+ '--version',
+ dest='version',
+ action='version',
+ version=__version__(),
+ help='''show version''')
+ parser.add_argument(
+ '--allow-empty-variables',
+ action='store_true',
+ dest='allow_empty_variables',
+ default=None,
+ help='''Don\'t fail if any of the variables inside any string are
+ not defined, replace with empty string instead.''')
+ parser.add_argument(
+ '--user', '-u',
+ help='''The Jenkins user to use for authentication. This overrides
+ the user specified in the configuration file.''')
+ parser.add_argument(
+ '--password', '-p',
+ help='''Password or API token to use for authenticating towards
+ Jenkins. This overrides the password specified in the configuration
+ file.''')
+
+ subparser = parser.add_subparsers(
+ dest='command',
+ help='''update, test or delete job''')
+
+ extension_manager = extension.ExtensionManager(
+ namespace='jjb.cli.subcommands',
+ invoke_on_load=True,
+ )
+
+ def parse_subcommand_args(ext, subparser):
+ ext.obj.parse_args(subparser)
+
+ extension_manager.map(parse_subcommand_args, subparser)
+
+ return parser
+
+ def execute(self):
+ jenkins_jobs.cmd.execute(self._options, self._config_file_values)
+
+
+def main():
+ argv = sys.argv[1:]
+ jjb = JenkinsJobs(argv)
+ jjb.execute()
diff --git a/jenkins_jobs/cli/subcommand/__init__.py b/jenkins_jobs/cli/subcommand/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/jenkins_jobs/cli/subcommand/base.py b/jenkins_jobs/cli/subcommand/base.py
new file mode 100644
index 000000000..f7de153f1
--- /dev/null
+++ b/jenkins_jobs/cli/subcommand/base.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# Copyright (C) 2015 Wayne Warren
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import abc
+import six
+
+
+@six.add_metaclass(abc.ABCMeta)
+class BaseSubCommand(object):
+ """Base class for Jenkins Job Builder subcommands, intended to allow
+ subcommands to be loaded as stevedore extensions by third party users.
+ """
+ def __init__(self):
+ pass
+
+ @abc.abstractmethod
+ def parse_args(self, subparsers, recursive_parser):
+ """Define subcommand arguments.
+
+ :param subparsers
+ A sub parser object. Implementations of this method should
+ create a new subcommand parser by calling
+ parser = subparsers.add_parser('command-name', ...)
+ This will return a new ArgumentParser object; all other arguments to
+ this method will be passed to the argparse.ArgumentParser constructor
+ for the returned object.
+ """
+
+ @abc.abstractmethod
+ def execute(self, config):
+ """Execute subcommand behavior.
+
+ :param config
+ JJBConfig object containing final configuration from config files,
+ command line arguments, and environment variables.
+ """
+
+ @staticmethod
+ def parse_option_recursive_exclude(parser):
+ """Add '--recursive' and '--exclude' arguments to given parser.
+ """
+ parser.add_argument(
+ '-r', '--recursive',
+ action='store_true',
+ dest='recursive',
+ default=False,
+ help='''look for yaml files recursively''')
+
+ parser.add_argument(
+ '-x', '--exclude',
+ dest='exclude',
+ action='append',
+ default=[],
+ help='''paths to exclude when using recursive search, uses standard
+ globbing.''')
diff --git a/jenkins_jobs/cli/subcommand/delete.py b/jenkins_jobs/cli/subcommand/delete.py
new file mode 100644
index 000000000..6092ca83b
--- /dev/null
+++ b/jenkins_jobs/cli/subcommand/delete.py
@@ -0,0 +1,23 @@
+
+import jenkins_jobs.cli.subcommand.base as base
+
+
+class DeleteSubCommand(base.BaseSubCommand):
+
+ def parse_args(self, subparser):
+ delete = subparser.add_parser('delete')
+
+ self.parse_option_recursive_exclude(delete)
+
+ delete.add_argument(
+ 'name',
+ help='name of job',
+ nargs='+')
+ delete.add_argument(
+ '-p', '--path',
+ default=None,
+ help='''colon-separated list of paths to YAML files or
+ directories''')
+
+ def execute(self, config):
+ raise NotImplementedError
diff --git a/jenkins_jobs/cli/subcommand/delete_all.py b/jenkins_jobs/cli/subcommand/delete_all.py
new file mode 100644
index 000000000..6cc3c6ab4
--- /dev/null
+++ b/jenkins_jobs/cli/subcommand/delete_all.py
@@ -0,0 +1,16 @@
+
+import jenkins_jobs.cli.subcommand.base as base
+
+
+class DeleteAllSubCommand(base.BaseSubCommand):
+
+ def parse_args(self, subparser):
+ delete_all = subparser.add_parser(
+ 'delete-all',
+ help='''delete *ALL* jobs from Jenkins server, including those not
+ managed by Jenkins Job Builder.''')
+
+ self.parse_option_recursive_exclude(delete_all)
+
+ def execute(self, config):
+ raise NotImplementedError
diff --git a/jenkins_jobs/cli/subcommand/test.py b/jenkins_jobs/cli/subcommand/test.py
new file mode 100644
index 000000000..ad52f4db3
--- /dev/null
+++ b/jenkins_jobs/cli/subcommand/test.py
@@ -0,0 +1,33 @@
+import sys
+
+import jenkins_jobs.cli.subcommand.base as base
+
+
+class TestSubCommand(base.BaseSubCommand):
+ def parse_args(self, subparser):
+ test = subparser.add_parser('test')
+
+ self.parse_option_recursive_exclude(test)
+
+ test.add_argument(
+ 'path',
+ help='''colon-separated list of paths to YAML files or
+ directories''',
+ nargs='?',
+ default=sys.stdin)
+ test.add_argument(
+ '-p',
+ dest='plugins_info_path',
+ default=None,
+ help='path to plugin info YAML file')
+ test.add_argument(
+ '-o',
+ dest='output_dir',
+ default=sys.stdout,
+ help='path to output XML')
+ test.add_argument(
+ 'name',
+ help='name(s) of job(s)', nargs='*')
+
+ def execute(self, config):
+ raise NotImplementedError
diff --git a/jenkins_jobs/cli/subcommand/update.py b/jenkins_jobs/cli/subcommand/update.py
new file mode 100644
index 000000000..1980c5631
--- /dev/null
+++ b/jenkins_jobs/cli/subcommand/update.py
@@ -0,0 +1,33 @@
+
+import jenkins_jobs.cli.subcommand.base as base
+
+
+class UpdateSubCommand(base.BaseSubCommand):
+ def parse_args(self, subparser):
+ update = subparser.add_parser('update')
+
+ self.parse_option_recursive_exclude(update)
+
+ update.add_argument(
+ 'path',
+ help='''colon-separated list of paths to YAML files or
+ directories''')
+ update.add_argument(
+ 'names',
+ help='name(s) of job(s)', nargs='*')
+ update.add_argument(
+ '--delete-old',
+ action='store_true',
+ dest='delete_old',
+ default=False,
+ help='delete obsolete jobs')
+ update.add_argument(
+ '--workers',
+ type=int,
+ default=1,
+ dest='n_workers',
+ help='''number of workers to use, 0 for autodetection and 1 for
+ just one worker.''')
+
+ def execute(self, config):
+ raise NotImplementedError
diff --git a/jenkins_jobs/cmd.py b/jenkins_jobs/cmd.py
index 5e54b27cb..0a5a20c42 100755
--- a/jenkins_jobs/cmd.py
+++ b/jenkins_jobs/cmd.py
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import argparse
import fnmatch
import io
import logging
@@ -31,6 +30,8 @@ from jenkins_jobs.errors import JenkinsJobsException
import jenkins_jobs.version
+import jenkins_jobs.cli
+
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger()
@@ -89,110 +90,7 @@ def recurse_path(root, excludes=None):
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')
- recursive_parser.add_argument('-x', '--exclude', dest='exclude',
- action='append', default=[],
- help='paths to exclude when using recursive'
- ' search, uses standard globbing.')
- subparser = parser.add_subparsers(help='update, test or delete job',
- dest='command')
-
- # subparser: update
- parser_update = subparser.add_parser('update', parents=[recursive_parser])
- parser_update.add_argument('path', help='colon-separated list of paths to'
- ' YAML files or directories')
- 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_update.add_argument('--workers', dest='n_workers', type=int,
- default=1, help='number of workers to use, 0 '
- 'for autodetection and 1 for just one worker.')
-
- # subparser: test
- parser_test = subparser.add_parser('test', parents=[recursive_parser])
- parser_test.add_argument('path', help='colon-separated list of paths to'
- ' YAML files or directories',
- nargs='?', default=sys.stdin)
- parser_test.add_argument('-p', dest='plugins_info_path', default=None,
- help='path to plugin info YAML file')
- parser_test.add_argument('-o', dest='output_dir', default=sys.stdout,
- help='path to output XML')
- parser_test.add_argument('name', help='name(s) of job(s)', nargs='*')
-
- # subparser: delete
- parser_delete = subparser.add_parser('delete', parents=[recursive_parser])
- parser_delete.add_argument('name', help='name of job', nargs='+')
- parser_delete.add_argument('-p', '--path', default=None,
- help='colon-separated list of paths to'
- ' YAML files or directories')
-
- # subparser: delete-all
- subparser.add_parser('delete-all',
- help='delete *ALL* jobs from Jenkins server, '
- 'including those not managed by Jenkins Job '
- 'Builder.')
- parser.add_argument('--conf', dest='conf', help='configuration file')
- parser.add_argument('-l', '--log_level', dest='log_level', default='info',
- help="log level (default: %(default)s)")
- parser.add_argument(
- '--ignore-cache', action='store_true',
- dest='ignore_cache', default=False,
- help='ignore the cache and update the jobs anyhow (that will only '
- 'flush the specified jobs cache)')
- parser.add_argument(
- '--flush-cache', action='store_true', dest='flush_cache',
- default=False, help='flush all the cache entries before updating')
- parser.add_argument('--version', dest='version', action='version',
- version=version(),
- help='show version')
- parser.add_argument(
- '--allow-empty-variables', action='store_true',
- dest='allow_empty_variables', default=None,
- help='Don\'t fail if any of the variables inside any string are not '
- 'defined, replace with empty string instead')
- parser.add_argument(
- '--user', '-u',
- help='The Jenkins user to use for authentication. This overrides '
- 'the user specified in the configuration file')
- parser.add_argument(
- '--password', '-p',
- help='Password or API token to use for authenticating towards '
- 'Jenkins. This overrides the password specified in the '
- 'configuration file.')
-
- return parser
-
-
-def main(argv=None):
-
- # We default argv to None and assign to sys.argv[1:] below because having
- # an argument default value be a mutable type in Python is a gotcha. See
- # http://bit.ly/1o18Vff
- if argv is None:
- argv = sys.argv[1:]
-
- parser = create_parser()
- options = parser.parse_args(argv)
- if not options.command:
- parser.error("Must specify a 'command' to be performed")
- if (options.log_level is not None):
- options.log_level = getattr(logging, options.log_level.upper(),
- logger.getEffectiveLevel())
- logger.setLevel(options.log_level)
-
- config = setup_config_settings(options)
- execute(options, config)
-
-
def get_config_file(options):
- # Initialize with the global fallback location for the config.
conf = '/etc/jenkins_jobs/jenkins_jobs.ini'
if options.conf:
conf = options.conf
@@ -211,7 +109,6 @@ def get_config_file(options):
def setup_config_settings(options):
-
conf = get_config_file(options)
config = configparser.ConfigParser()
# Load default config always
@@ -225,8 +122,8 @@ def setup_config_settings(options):
logger.debug("Not requiring config for test output generation")
else:
raise JenkinsJobsException(
- "A valid configuration file is required when not run as a test"
- "\n{0} is not a valid .ini file".format(conf))
+ "A valid configuration file is required."
+ "\n{0} is not valid.".format(conf))
return config
@@ -378,13 +275,3 @@ def execute(options, config):
builder.update_jobs(options.path, options.name,
output=options.output_dir,
n_workers=1)
-
-
-def version():
- return "Jenkins Job Builder version: %s" % \
- jenkins_jobs.version.version_info.version_string()
-
-
-if __name__ == '__main__':
- sys.path.insert(0, '.')
- main()
diff --git a/requirements.txt b/requirements.txt
index 57c3a3776..0be6b339e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,3 +2,4 @@ six>=1.5.2
PyYAML
python-jenkins>=0.4.8
pbr>=1.0.0,<2.0
+stevedore==1.8.0
diff --git a/setup.cfg b/setup.cfg
index 841bbccc4..69ac987aa 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -34,7 +34,12 @@ warnerrors = True
[entry_points]
console_scripts =
- jenkins-jobs=jenkins_jobs.cmd:main
+ jenkins-jobs=jenkins_jobs.cli.entry:main
+jjb.cli.subcommands =
+ update=jenkins_jobs.cli.subcommand.update:UpdateSubCommand
+ test=jenkins_jobs.cli.subcommand.test:TestSubCommand
+ delete=jenkins_jobs.cli.subcommand.delete:DeleteSubCommand
+ delet-all=jenkins_jobs.cli.subcommand.delete_all:DeleteAllSubCommand
jenkins_jobs.projects =
externaljob=jenkins_jobs.modules.project_externaljob:ExternalJob
flow=jenkins_jobs.modules.project_flow:Flow
diff --git a/tests/cmd/fixtures/empty_builder.ini b/tests/cmd/fixtures/empty_builder.ini
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/cmd/fixtures/multi-path/builder-recursive.ini b/tests/cmd/fixtures/multi-path/builder-recursive.ini
new file mode 100644
index 000000000..62c674b98
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/builder-recursive.ini
@@ -0,0 +1,2 @@
+[job_builder]
+recursive=True
\ No newline at end of file
diff --git a/tests/cmd/fixtures/multi-path/output_recursive/job1 b/tests/cmd/fixtures/multi-path/output_recursive/job1
new file mode 100644
index 000000000..0570e10a5
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/output_recursive/job1
@@ -0,0 +1,15 @@
+
+
+
+ <!-- Managed by Jenkins Job Builder -->
+ false
+ false
+ false
+ false
+ true
+
+
+
+
+
+
diff --git a/tests/cmd/fixtures/multi-path/output_recursive/job2 b/tests/cmd/fixtures/multi-path/output_recursive/job2
new file mode 100644
index 000000000..8b8552a49
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/output_recursive/job2
@@ -0,0 +1,16 @@
+
+
+
+ <!-- Managed by Jenkins Job Builder -->
+ false
+ true
+ false
+ false
+ false
+ true
+
+
+
+
+
+
diff --git a/tests/cmd/fixtures/multi-path/output_recursive/job3 b/tests/cmd/fixtures/multi-path/output_recursive/job3
new file mode 100644
index 000000000..beab678e8
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/output_recursive/job3
@@ -0,0 +1,17 @@
+
+
+
+ <!-- Managed by Jenkins Job Builder -->
+ false
+ true
+ herp derp derp
+ false
+ false
+ false
+ true
+
+
+
+
+
+
diff --git a/tests/cmd/fixtures/multi-path/output_recursive/job4 b/tests/cmd/fixtures/multi-path/output_recursive/job4
new file mode 100644
index 000000000..dd47f9624
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/output_recursive/job4
@@ -0,0 +1,20 @@
+
+
+
+ false
+
+
+
+
+ <!-- Managed by Jenkins Job Builder -->
+ false
+ false
+ false
+ false
+ true
+
+
+
+
+
+
diff --git a/tests/cmd/fixtures/multi-path/output_recursive_with_excludes/job1 b/tests/cmd/fixtures/multi-path/output_recursive_with_excludes/job1
new file mode 100644
index 000000000..0570e10a5
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/output_recursive_with_excludes/job1
@@ -0,0 +1,15 @@
+
+
+
+ <!-- Managed by Jenkins Job Builder -->
+ false
+ false
+ false
+ false
+ true
+
+
+
+
+
+
diff --git a/tests/cmd/fixtures/multi-path/output_recursive_with_excludes/job2 b/tests/cmd/fixtures/multi-path/output_recursive_with_excludes/job2
new file mode 100644
index 000000000..8b8552a49
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/output_recursive_with_excludes/job2
@@ -0,0 +1,16 @@
+
+
+
+ <!-- Managed by Jenkins Job Builder -->
+ false
+ true
+ false
+ false
+ false
+ true
+
+
+
+
+
+
diff --git a/tests/cmd/fixtures/multi-path/output_recursive_with_excludes/job3 b/tests/cmd/fixtures/multi-path/output_recursive_with_excludes/job3
new file mode 100644
index 000000000..beab678e8
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/output_recursive_with_excludes/job3
@@ -0,0 +1,17 @@
+
+
+
+ <!-- Managed by Jenkins Job Builder -->
+ false
+ true
+ herp derp derp
+ false
+ false
+ false
+ true
+
+
+
+
+
+
diff --git a/tests/cmd/fixtures/multi-path/output_simple/job2 b/tests/cmd/fixtures/multi-path/output_simple/job2
new file mode 100644
index 000000000..8b8552a49
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/output_simple/job2
@@ -0,0 +1,16 @@
+
+
+
+ <!-- Managed by Jenkins Job Builder -->
+ false
+ true
+ false
+ false
+ false
+ true
+
+
+
+
+
+
diff --git a/tests/cmd/fixtures/multi-path/output_simple/job3 b/tests/cmd/fixtures/multi-path/output_simple/job3
new file mode 100644
index 000000000..beab678e8
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/output_simple/job3
@@ -0,0 +1,17 @@
+
+
+
+ <!-- Managed by Jenkins Job Builder -->
+ false
+ true
+ herp derp derp
+ false
+ false
+ false
+ true
+
+
+
+
+
+
diff --git a/tests/cmd/fixtures/multi-path/yamldirs/dir1/dir1/project.yaml b/tests/cmd/fixtures/multi-path/yamldirs/dir1/dir1/project.yaml
new file mode 100644
index 000000000..71688468f
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/yamldirs/dir1/dir1/project.yaml
@@ -0,0 +1,7 @@
+- project:
+ name: 'hello'
+ jobs:
+ - 'job1'
+
+- job:
+ name: 'job1'
\ No newline at end of file
diff --git a/tests/cmd/fixtures/multi-path/yamldirs/dir1/project.yaml b/tests/cmd/fixtures/multi-path/yamldirs/dir1/project.yaml
new file mode 100644
index 000000000..33c5f36f7
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/yamldirs/dir1/project.yaml
@@ -0,0 +1,8 @@
+- project:
+ name: 'beep'
+ jobs:
+ - 'job2'
+
+- job:
+ name: 'job2'
+ disabled: True
\ No newline at end of file
diff --git a/tests/cmd/fixtures/multi-path/yamldirs/dir2/dir1/project.yaml b/tests/cmd/fixtures/multi-path/yamldirs/dir2/dir1/project.yaml
new file mode 100644
index 000000000..d579bb652
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/yamldirs/dir2/dir1/project.yaml
@@ -0,0 +1,8 @@
+- project:
+ name: 'goodbye'
+ jobs:
+ - 'job4'
+
+- job:
+ name: 'job4'
+ project-type: matrix
\ No newline at end of file
diff --git a/tests/cmd/fixtures/multi-path/yamldirs/dir2/project.yaml b/tests/cmd/fixtures/multi-path/yamldirs/dir2/project.yaml
new file mode 100644
index 000000000..6c625407d
--- /dev/null
+++ b/tests/cmd/fixtures/multi-path/yamldirs/dir2/project.yaml
@@ -0,0 +1,9 @@
+- project:
+ name: 'meow'
+ jobs:
+ - 'job3'
+
+- job:
+ name: 'job3'
+ display-name: 'herp derp derp'
+ disabled: True
\ No newline at end of file
diff --git a/tests/cmd/fixtures/non-default-timeout.ini b/tests/cmd/fixtures/non-default-timeout.ini
new file mode 100644
index 000000000..32f8438e5
--- /dev/null
+++ b/tests/cmd/fixtures/non-default-timeout.ini
@@ -0,0 +1,2 @@
+[jenkins]
+timeout=0.2
\ No newline at end of file
diff --git a/tests/cmd/subcommands/test_delete.py b/tests/cmd/subcommands/test_delete.py
index a6d47756e..45b128a79 100644
--- a/tests/cmd/subcommands/test_delete.py
+++ b/tests/cmd/subcommands/test_delete.py
@@ -1,6 +1,5 @@
import os
-from jenkins_jobs import cmd
from tests.base import mock
from tests.cmd.test_cmd import CmdTestsBase
@@ -14,8 +13,8 @@ class DeleteTests(CmdTestsBase):
Test handling the deletion of a single Jenkins job.
"""
- args = self.parser.parse_args(['delete', 'test_job'])
- cmd.execute(args, self.config) # passes if executed without error
+ args = ['--conf', self.default_config_file, 'delete', 'test_job']
+ self.execute_jenkins_jobs_with_args(args)
@mock.patch('jenkins_jobs.cmd.Builder.delete_job')
def test_delete_multiple_jobs(self, delete_job_mock):
@@ -23,8 +22,9 @@ class DeleteTests(CmdTestsBase):
Test handling the deletion of multiple Jenkins jobs.
"""
- args = self.parser.parse_args(['delete', 'test_job1', 'test_job2'])
- cmd.execute(args, self.config) # passes if executed without error
+ args = ['--conf', self.default_config_file,
+ 'delete', 'test_job1', 'test_job2']
+ self.execute_jenkins_jobs_with_args(args)
@mock.patch('jenkins_jobs.builder.Jenkins.delete_job')
def test_delete_using_glob_params(self, delete_job_mock):
@@ -33,12 +33,12 @@ class DeleteTests(CmdTestsBase):
parameters feature.
"""
- args = self.parser.parse_args(['delete',
- '--path',
- os.path.join(self.fixtures_path,
- 'cmd-002.yaml'),
- '*bar*'])
- cmd.execute(args, self.config)
+ args = ['--conf', self.default_config_file,
+ 'delete', '--path',
+ os.path.join(self.fixtures_path,
+ 'cmd-002.yaml'),
+ '*bar*']
+ self.execute_jenkins_jobs_with_args(args)
calls = [mock.call('bar001'), mock.call('bar002')]
delete_job_mock.assert_has_calls(calls, any_order=True)
self.assertEqual(delete_job_mock.call_count, len(calls),
diff --git a/tests/cmd/subcommands/test_test.py b/tests/cmd/subcommands/test_test.py
index b48d4a3e2..5de90b814 100644
--- a/tests/cmd/subcommands/test_test.py
+++ b/tests/cmd/subcommands/test_test.py
@@ -1,194 +1,43 @@
+import difflib
+import filecmp
import io
import os
+import shutil
+import tempfile
import yaml
import jenkins
+import testtools
-from jenkins_jobs import cmd
+from jenkins_jobs.cli import entry
from jenkins_jobs.errors import JenkinsJobsException
-from mock import patch
from tests.base import mock
from tests.cmd.test_cmd import CmdTestsBase
-from tests.cmd.test_recurse_path import fake_os_walk
-
-os_walk_return_values = {
- '/jjb_projects': [
- ('/jjb_projects', (['dir1', 'dir2', 'dir3'], ())),
- ('/jjb_projects/dir1', (['bar'], ())),
- ('/jjb_projects/dir2', (['baz'], ())),
- ('/jjb_projects/dir3', ([], ())),
- ('/jjb_projects/dir1/bar', ([], ())),
- ('/jjb_projects/dir2/baz', ([], ())),
- ],
- '/jjb_templates': [
- ('/jjb_templates', (['dir1', 'dir2', 'dir3'], ())),
- ('/jjb_templates/dir1', (['bar'], ())),
- ('/jjb_templates/dir2', (['baz'], ())),
- ('/jjb_templates/dir3', ([], ())),
- ('/jjb_templates/dir1/bar', ([], ())),
- ('/jjb_templates/dir2/baz', ([], ())),
- ],
- '/jjb_macros': [
- ('/jjb_macros', (['dir1', 'dir2', 'dir3'], ())),
- ('/jjb_macros/dir1', (['bar'], ())),
- ('/jjb_macros/dir2', (['baz'], ())),
- ('/jjb_macros/dir3', ([], ())),
- ('/jjb_macros/dir1/bar', ([], ())),
- ('/jjb_macros/dir2/baz', ([], ())),
- ],
-}
-
-
-def os_walk_side_effects(path_name, topdown):
- return fake_os_walk(os_walk_return_values[path_name])(path_name, topdown)
-
-
-@mock.patch('jenkins_jobs.builder.Jenkins.get_plugins_info', mock.MagicMock)
-class TestConfigs(CmdTestsBase):
-
- def test_use_global_config(self):
- """
- Verify that JJB uses the global config file by default
- """
- args = self.parser.parse_args(['test', 'foo'])
- self.assertEqual(cmd.get_config_file(args),
- '/etc/jenkins_jobs/jenkins_jobs.ini')
-
- def test_use_config_in_user_home(self):
- """
- Verify that JJB uses config file in user home folder
- """
- args = self.parser.parse_args(['test', 'foo'])
- # args.output_dir = mock.MagicMock()
- # mock_isfile.side_effect = True
- expected_loc = os.path.join(os.path.expanduser('~'), '.config',
- 'jenkins_jobs', 'jenkins_jobs.ini')
- with patch('os.path.isfile', return_value=True):
- self.assertEqual(cmd.get_config_file(args), expected_loc)
@mock.patch('jenkins_jobs.builder.Jenkins.get_plugins_info', mock.MagicMock)
class TestTests(CmdTestsBase):
- def test_non_existing_config_dir(self):
- """
- Run test mode and pass a non-existing configuration directory
- """
- args = self.parser.parse_args(['test', 'foo'])
- args.output_dir = mock.MagicMock()
- self.assertRaises(IOError, cmd.execute, args, self.config)
-
- def test_non_existing_config_file(self):
- """
- Run test mode and pass a non-existing configuration file
- """
- args = self.parser.parse_args(['test', 'non-existing.yaml'])
- args.output_dir = mock.MagicMock()
- self.assertRaises(IOError, cmd.execute, args, self.config)
-
def test_non_existing_job(self):
"""
Run test mode and pass a non-existing job name
(probably better to fail here)
"""
- args = self.parser.parse_args(['test',
- os.path.join(self.fixtures_path,
- 'cmd-001.yaml'),
- 'invalid'])
- args.output_dir = mock.MagicMock(wraps=io.BytesIO())
- cmd.execute(args, self.config) # probably better to fail here
+ args = ['--conf', self.default_config_file, 'test',
+ os.path.join(self.fixtures_path,
+ 'cmd-001.yaml'),
+ 'invalid']
+ self.execute_jenkins_jobs_with_args(args)
def test_valid_job(self):
"""
Run test mode and pass a valid job name
"""
- args = self.parser.parse_args(['test',
- os.path.join(self.fixtures_path,
- 'cmd-001.yaml'),
- 'foo-job'])
- args.output_dir = mock.Mock(wraps=io.BytesIO())
- cmd.execute(args, self.config) # probably better to fail here
-
- @mock.patch('jenkins_jobs.cmd.Builder.update_jobs')
- def test_multi_path(self, update_jobs_mock):
- """
- Run test mode and pass multiple paths.
- """
- path_list = list(os_walk_return_values.keys())
- multipath = os.pathsep.join(path_list)
-
- args = self.parser.parse_args(['test', multipath])
- args.output_dir = mock.MagicMock()
-
- cmd.execute(args, self.config)
- self.assertEqual(args.path, path_list)
- update_jobs_mock.assert_called_with(path_list, [],
- output=args.output_dir,
- n_workers=mock.ANY)
-
- @mock.patch('jenkins_jobs.cmd.Builder.update_jobs')
- @mock.patch('jenkins_jobs.cmd.os.path.isdir')
- @mock.patch('jenkins_jobs.cmd.os.walk')
- def test_recursive_multi_path(self, os_walk_mock, isdir_mock,
- update_jobs_mock):
- """
- Run test mode and pass multiple paths with recursive path option.
- """
-
- os_walk_mock.side_effect = os_walk_side_effects
- isdir_mock.return_value = True
-
- path_list = os_walk_return_values.keys()
- paths = []
- for path in path_list:
- paths.extend([p for p, _ in os_walk_return_values[path]])
-
- multipath = os.pathsep.join(path_list)
-
- args = self.parser.parse_args(['test', '-r', multipath])
- args.output_dir = mock.MagicMock()
-
- cmd.execute(args, self.config)
-
- update_jobs_mock.assert_called_with(paths, [], output=args.output_dir,
- n_workers=mock.ANY)
-
- args = self.parser.parse_args(['test', multipath])
- args.output_dir = mock.MagicMock()
- self.config.set('job_builder', 'recursive', 'True')
- cmd.execute(args, self.config)
-
- update_jobs_mock.assert_called_with(paths, [], output=args.output_dir,
- n_workers=mock.ANY)
-
- @mock.patch('jenkins_jobs.cmd.Builder.update_jobs')
- @mock.patch('jenkins_jobs.cmd.os.path.isdir')
- @mock.patch('jenkins_jobs.cmd.os.walk')
- def test_recursive_multi_path_with_excludes(self, os_walk_mock, isdir_mock,
- update_jobs_mock):
- """
- Run test mode and pass multiple paths with recursive path option.
- """
-
- os_walk_mock.side_effect = os_walk_side_effects
- isdir_mock.return_value = True
-
- path_list = os_walk_return_values.keys()
- paths = []
- for path in path_list:
- paths.extend([p for p, __ in os_walk_return_values[path]
- if 'dir1' not in p and 'dir2' not in p])
-
- multipath = os.pathsep.join(path_list)
-
- args = self.parser.parse_args(['test', '-r', multipath, '-x',
- 'dir1:dir2'])
- args.output_dir = mock.MagicMock()
-
- cmd.execute(args, self.config)
-
- update_jobs_mock.assert_called_with(paths, [], output=args.output_dir,
- n_workers=mock.ANY)
+ args = ['--conf', self.default_config_file, 'test',
+ os.path.join(self.fixtures_path,
+ 'cmd-001.yaml'),
+ 'foo-job']
+ self.execute_jenkins_jobs_with_args(args)
def test_console_output(self):
"""
@@ -197,8 +46,9 @@ class TestTests(CmdTestsBase):
console_out = io.BytesIO()
with mock.patch('sys.stdout', console_out):
- cmd.main(['test', os.path.join(self.fixtures_path,
- 'cmd-001.yaml')])
+ args = ['--conf', self.default_config_file, 'test',
+ os.path.join(self.fixtures_path, 'cmd-001.yaml')]
+ self.execute_jenkins_jobs_with_args(args)
xml_content = io.open(os.path.join(self.fixtures_path, 'cmd-001.xml'),
'r', encoding='utf-8').read()
self.assertEqual(console_out.getvalue().decode('utf-8'), xml_content)
@@ -214,7 +64,8 @@ class TestTests(CmdTestsBase):
with io.open(input_file, 'r') as f:
with mock.patch('sys.stdout', console_out):
with mock.patch('sys.stdin', f):
- cmd.main(['test'])
+ args = ['--conf', self.default_config_file, 'test']
+ self.execute_jenkins_jobs_with_args(args)
xml_content = io.open(os.path.join(self.fixtures_path, 'cmd-001.xml'),
'r', encoding='utf-8').read()
@@ -233,7 +84,8 @@ class TestTests(CmdTestsBase):
with io.open(input_file, 'r') as f:
with mock.patch('sys.stdout', console_out):
with mock.patch('sys.stdin', f):
- cmd.main(['test'])
+ args = ['--conf', self.default_config_file, 'test']
+ self.execute_jenkins_jobs_with_args(args)
xml_content = io.open(os.path.join(self.fixtures_path, 'cmd-001.xml'),
'r', encoding='utf-8').read()
@@ -253,24 +105,11 @@ class TestTests(CmdTestsBase):
with io.open(input_file, 'r', encoding='utf-8') as f:
with mock.patch('sys.stdout', console_out):
with mock.patch('sys.stdin', f):
- e = self.assertRaises(UnicodeError, cmd.main, ['test'])
+ args = ['--conf', self.default_config_file, 'test']
+ jenkins_jobs = entry.JenkinsJobs(args)
+ e = self.assertRaises(UnicodeError, jenkins_jobs.execute)
self.assertIn("'ascii' codec can't encode character", str(e))
- def test_config_with_test(self):
- """
- Run test mode and pass a config file
- """
- args = self.parser.parse_args(['--conf',
- os.path.join(self.fixtures_path,
- 'cmd-001.conf'),
- 'test',
- os.path.join(self.fixtures_path,
- 'cmd-001.yaml'),
- 'foo-job'])
- config = cmd.setup_config_settings(args)
- self.assertEqual(config.get('jenkins', 'url'),
- "http://test-jenkins.with.non.default.url:8080/")
-
@mock.patch('jenkins_jobs.builder.YamlParser.generateXML')
@mock.patch('jenkins_jobs.parser.ModuleRegistry')
def test_plugins_info_stub_option(self, registry_mock, generateXML_mock):
@@ -285,16 +124,15 @@ class TestTests(CmdTestsBase):
'-p',
plugins_info_stub_yaml_file,
os.path.join(self.fixtures_path, 'cmd-001.yaml')]
- args = self.parser.parse_args(args)
- with mock.patch('sys.stdout'):
- cmd.execute(args, self.config) # probably better to fail here
+ self.execute_jenkins_jobs_with_args(args)
with io.open(plugins_info_stub_yaml_file,
'r', encoding='utf-8') as yaml_file:
plugins_info_list = yaml.load(yaml_file)
- registry_mock.assert_called_with(self.config, plugins_info_list)
+ registry_mock.assert_called_with(mock.ANY,
+ plugins_info_list)
@mock.patch('jenkins_jobs.builder.YamlParser.generateXML')
@mock.patch('jenkins_jobs.parser.ModuleRegistry')
@@ -312,11 +150,10 @@ class TestTests(CmdTestsBase):
'-p',
plugins_info_stub_yaml_file,
os.path.join(self.fixtures_path, 'cmd-001.yaml')]
- args = self.parser.parse_args(args)
with mock.patch('sys.stdout'):
- e = self.assertRaises(JenkinsJobsException, cmd.execute,
- args, self.config)
+ jenkins_jobs = entry.JenkinsJobs(args)
+ e = self.assertRaises(JenkinsJobsException, jenkins_jobs.execute)
self.assertIn("must contain a Yaml list", str(e))
@@ -341,8 +178,9 @@ class TestJenkinsGetPluginInfoError(CmdTestsBase):
jenkins.JenkinsException("Connection refused")
with mock.patch('sys.stdout'):
try:
- cmd.main(['test', os.path.join(self.fixtures_path,
- 'cmd-001.yaml')])
+ args = ['--conf', self.default_config_file, 'test',
+ os.path.join(self.fixtures_path, 'cmd-001.yaml')]
+ self.execute_jenkins_jobs_with_args(args)
except jenkins.JenkinsException:
self.fail("jenkins.JenkinsException propagated to main")
except:
@@ -356,8 +194,9 @@ class TestJenkinsGetPluginInfoError(CmdTestsBase):
plugins will be skipped when run if no config file provided.
"""
with mock.patch('sys.stdout', new_callable=io.BytesIO):
- cmd.main(['test', os.path.join(self.fixtures_path,
- 'cmd-001.yaml')])
+ args = ['--conf', self.default_config_file, 'test',
+ os.path.join(self.fixtures_path, 'cmd-001.yaml')]
+ entry.JenkinsJobs(args)
self.assertFalse(get_plugins_info_mock.called)
@mock.patch('jenkins.Jenkins.get_plugins_info')
@@ -368,9 +207,143 @@ class TestJenkinsGetPluginInfoError(CmdTestsBase):
querying through a config option.
"""
with mock.patch('sys.stdout', new_callable=io.BytesIO):
- cmd.main(['--conf',
- os.path.join(self.fixtures_path,
- 'disable-query-plugins.conf'),
- 'test',
- os.path.join(self.fixtures_path, 'cmd-001.yaml')])
+ args = ['--conf',
+ os.path.join(self.fixtures_path,
+ 'disable-query-plugins.conf'),
+ 'test',
+ os.path.join(self.fixtures_path, 'cmd-001.yaml')]
+ entry.JenkinsJobs(args)
self.assertFalse(get_plugins_info_mock.called)
+
+
+class MatchesDirMissingFilesMismatch(object):
+ def __init__(self, left_directory, right_directory):
+ self.left_directory = left_directory
+ self.right_directory = right_directory
+
+ def describe(self):
+ return "{0} and {1} contain different files".format(
+ self.left_directory,
+ self.right_directory)
+
+ def get_details(self):
+ return {}
+
+
+class MatchesDirFileContentsMismatch(object):
+ def __init__(self, left_file, right_file):
+ self.left_file = left_file
+ self.right_file = right_file
+
+ def describe(self):
+ left_contents = open(self.left_file).readlines()
+ right_contents = open(self.right_file).readlines()
+
+ return "{0} is not equal to {1}:\n{2}".format(
+ difflib.unified_diff(left_contents, right_contents,
+ fromfile=self.left_file,
+ tofile=self.right_file),
+ self.left_file,
+ self.right_file)
+
+ def get_details(self):
+ return {}
+
+
+class MatchesDir(object):
+ def __init__(self, directory):
+ self.__directory = directory
+ self.__files = self.__get_files(directory)
+
+ def __get_files(self, directory):
+ for root, _, files in os.walk(directory):
+ return files
+
+ def __str__(self,):
+ return "MatchesDir({0})".format(self.__dirname)
+
+ def match(self, other_directory):
+ other_files = self.__get_files(other_directory)
+
+ self.__files.sort()
+ other_files.sort()
+
+ if self.__files != other_files:
+ return MatchesDirMissingFilesMismatch(self.__directory,
+ other_directory)
+
+ for i, file in enumerate(self.__files):
+ my_file = os.path.join(self.__directory, file)
+ other_file = os.path.join(other_directory, other_files[i])
+ if not filecmp.cmp(my_file, other_file):
+ return MatchesDirFileContentsMismatch(my_file, other_file)
+
+ return None
+
+
+@mock.patch('jenkins_jobs.builder.Jenkins.get_plugins_info', mock.MagicMock)
+class TestTestsMultiPath(CmdTestsBase):
+
+ def setUp(self):
+ super(TestTestsMultiPath, self).setUp()
+
+ path_list = [os.path.join(self.fixtures_path,
+ 'multi-path/yamldirs/', p)
+ for p in ['dir1', 'dir2']]
+ self.multipath = os.pathsep.join(path_list)
+ self.output_dir = tempfile.mkdtemp()
+
+ def check_dirs_match(self, expected_dir):
+ try:
+ self.assertThat(self.output_dir, MatchesDir(expected_dir))
+ except testtools.matchers.MismatchError as e:
+ raise e
+ else:
+ shutil.rmtree(self.output_dir)
+
+ def test_multi_path(self):
+ """
+ Run test mode and pass multiple paths.
+ """
+ args = ['--conf', self.default_config_file, 'test',
+ '-o', self.output_dir, self.multipath]
+
+ self.execute_jenkins_jobs_with_args(args)
+ self.check_dirs_match(os.path.join(self.fixtures_path,
+ 'multi-path/output_simple'))
+
+ def test_recursive_multi_path_command_line(self):
+ """
+ Run test mode and pass multiple paths with recursive path option.
+ """
+ args = ['--conf', self.default_config_file, 'test',
+ '-o', self.output_dir, '-r', self.multipath]
+
+ self.execute_jenkins_jobs_with_args(args)
+ self.check_dirs_match(os.path.join(self.fixtures_path,
+ 'multi-path/output_recursive'))
+
+ def test_recursive_multi_path_config_file(self):
+ # test recursive set in configuration file
+ args = ['--conf', os.path.join(self.fixtures_path,
+ 'multi-path/builder-recursive.ini'),
+ 'test', '-o', self.output_dir, self.multipath]
+ self.execute_jenkins_jobs_with_args(args)
+ self.check_dirs_match(os.path.join(self.fixtures_path,
+ 'multi-path/output_recursive'))
+
+ def test_recursive_multi_path_with_excludes(self):
+ """
+ Run test mode and pass multiple paths with recursive path option.
+ """
+ exclude_path = os.path.join(self.fixtures_path,
+ 'multi-path/yamldirs/dir2/dir1')
+ args = ['--conf', self.default_config_file, 'test',
+ '-x', exclude_path,
+ '-o', self.output_dir,
+ '-r', self.multipath]
+
+ self.execute_jenkins_jobs_with_args(args)
+ self.check_dirs_match(
+ os.path.join(self.fixtures_path,
+ 'multi-path/output_recursive_with_excludes'))
diff --git a/tests/cmd/subcommands/test_update.py b/tests/cmd/subcommands/test_update.py
index 9f08a03a2..10874aea7 100644
--- a/tests/cmd/subcommands/test_update.py
+++ b/tests/cmd/subcommands/test_update.py
@@ -18,7 +18,6 @@ import os
import six
from jenkins_jobs import builder
-from jenkins_jobs import cmd
from tests.base import mock
from tests.cmd.test_cmd import CmdTestsBase
@@ -35,9 +34,9 @@ class UpdateTests(CmdTestsBase):
update_jobs_mock.return_value = ([], 0)
path = os.path.join(self.fixtures_path, 'cmd-002.yaml')
- args = self.parser.parse_args(['update', path])
+ args = ['--conf', self.default_config_file, 'update', path]
- cmd.execute(args, self.config)
+ self.execute_jenkins_jobs_with_args(args)
update_jobs_mock.assert_called_with([path], [], n_workers=mock.ANY)
@mock.patch('jenkins_jobs.builder.Jenkins.is_job', return_value=True)
@@ -54,9 +53,9 @@ class UpdateTests(CmdTestsBase):
update_job_mock.return_value = ([], 0)
path = os.path.join(self.fixtures_path, 'cmd-002.yaml')
- args = self.parser.parse_args(['update', path])
+ args = ['--conf', self.default_config_file, 'update', path]
- cmd.execute(args, self.config)
+ self.execute_jenkins_jobs_with_args(args)
self.assertTrue(isinstance(update_job_mock.call_args[0][1],
six.text_type))
@@ -101,17 +100,18 @@ class UpdateTests(CmdTestsBase):
[True] * 2 + [False] * 2)
path = os.path.join(self.fixtures_path, 'cmd-002.yaml')
- args = self.parser.parse_args(['update', '--delete-old', path])
+ args = ['--conf', self.default_config_file, 'update', '--delete-old',
+ path]
with mock.patch('jenkins_jobs.builder.Jenkins.update_job') as update:
with mock.patch('jenkins_jobs.builder.Jenkins.is_managed',
return_value=True):
- cmd.execute(args, self.config)
- self.assertEqual(2, update.call_count,
- "Expected Jenkins.update_job to be called '%d' "
- "times, got '%d' calls instead.\n"
- "Called with: %s" % (2, update.call_count,
- update.mock_calls))
+ self.execute_jenkins_jobs_with_args(args)
+ self.assertEquals(2, update.call_count,
+ "Expected Jenkins.update_job to be called '%d' "
+ "times, got '%d' calls instead.\n"
+ "Called with: %s" % (2, update.call_count,
+ update.mock_calls))
calls = [mock.call(name) for name in jobs]
self.assertEqual(2, delete_job_mock.call_count,
@@ -130,11 +130,11 @@ class UpdateTests(CmdTestsBase):
"""
path = os.path.join(self.fixtures_path, 'cmd-002.yaml')
- args = self.parser.parse_args(['update', path])
+ args = ['--conf', self.default_config_file, 'update', path]
with mock.patch('jenkins_jobs.cmd.Builder.update_job') as update_mock:
update_mock.return_value = ([], 0)
- cmd.execute(args, self.config)
+ self.execute_jenkins_jobs_with_args(args)
# unless the timeout is set, should only call with 3 arguments
# (url, user, password)
self.assertEqual(len(jenkins_mock.call_args[0]), 3)
@@ -148,12 +148,13 @@ class UpdateTests(CmdTestsBase):
"""
path = os.path.join(self.fixtures_path, 'cmd-002.yaml')
- args = self.parser.parse_args(['update', path])
- self.config.set('jenkins', 'timeout', '0.2')
+ config_file = os.path.join(self.fixtures_path,
+ 'non-default-timeout.ini')
+ args = ['--conf', config_file, 'update', path]
with mock.patch('jenkins_jobs.cmd.Builder.update_job') as update_mock:
update_mock.return_value = ([], 0)
- cmd.execute(args, self.config)
+ self.execute_jenkins_jobs_with_args(args)
# when timeout is set, the fourth argument to the Jenkins api init
# should be the value specified from the config
self.assertEqual(jenkins_mock.call_args[0][3], 0.2)
diff --git a/tests/cmd/test_cmd.py b/tests/cmd/test_cmd.py
index eb55b668e..b21776150 100644
--- a/tests/cmd/test_cmd.py
+++ b/tests/cmd/test_cmd.py
@@ -1,10 +1,7 @@
import os
-from six.moves import configparser
-from six.moves import StringIO
import testtools
-
-from jenkins_jobs import cmd
+from jenkins_jobs.cli import entry
from tests.base import LoggingFixture
from tests.base import mock
@@ -12,7 +9,6 @@ from tests.base import mock
class CmdTestsBase(LoggingFixture, testtools.TestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures')
- parser = cmd.create_parser()
def setUp(self):
super(CmdTestsBase, self).setUp()
@@ -27,8 +23,12 @@ class CmdTestsBase(LoggingFixture, testtools.TestCase):
self.cache_mock = cache_patch.start()
self.addCleanup(cache_patch.stop)
- self.config = configparser.ConfigParser()
- self.config.readfp(StringIO(cmd.DEFAULT_CONF))
+ self.default_config_file = os.path.join(self.fixtures_path,
+ 'empty_builder.ini')
+
+ def execute_jenkins_jobs_with_args(self, args):
+ jenkins_jobs = entry.JenkinsJobs(args)
+ jenkins_jobs.execute()
class CmdTests(CmdTestsBase):
@@ -38,4 +38,4 @@ class CmdTests(CmdTestsBase):
User passes no args, should fail with SystemExit
"""
with mock.patch('sys.stderr'):
- self.assertRaises(SystemExit, cmd.main, [])
+ self.assertRaises(SystemExit, entry.JenkinsJobs, [])
diff --git a/tests/cmd/test_config.py b/tests/cmd/test_config.py
new file mode 100644
index 000000000..de49353bf
--- /dev/null
+++ b/tests/cmd/test_config.py
@@ -0,0 +1,73 @@
+import io
+import os
+
+from jenkins_jobs.cli import entry
+from mock import patch
+from tests.base import mock
+from tests.cmd.test_cmd import CmdTestsBase
+
+
+@mock.patch('jenkins_jobs.builder.Jenkins.get_plugins_info', mock.MagicMock)
+class TestConfigs(CmdTestsBase):
+
+ global_conf = '/etc/jenkins_jobs/jenkins_jobs.ini'
+ user_conf = os.path.join(os.path.expanduser('~'), '.config',
+ 'jenkins_jobs', 'jenkins_jobs.ini')
+
+ def test_use_global_config(self):
+ """
+ Verify that JJB uses the global config file by default
+ """
+
+ args = ['test', 'foo']
+ conffp = io.open(self.default_config_file, 'r', encoding='utf-8')
+
+ with patch('os.path.isfile', return_value=True) as m_isfile:
+ def side_effect(path):
+ if path == self.user_conf:
+ return False
+ if path == self.global_conf:
+ return True
+
+ m_isfile.side_effect = side_effect
+
+ with patch('io.open', return_value=conffp) as m_open:
+ entry.JenkinsJobs(args)
+ m_open.assert_called_with(self.global_conf, 'r',
+ encoding='utf-8')
+
+ def test_use_config_in_user_home(self):
+ """
+ Verify that JJB uses config file in user home folder
+ """
+
+ args = ['test', 'foo']
+
+ conffp = io.open(self.default_config_file, 'r', encoding='utf-8')
+ with patch('os.path.isfile', return_value=True) as m_isfile:
+ def side_effect(path):
+ if path == self.user_conf:
+ return True
+
+ m_isfile.side_effect = side_effect
+ with patch('io.open', return_value=conffp) as m_open:
+ entry.JenkinsJobs(args)
+ m_open.assert_called_with(self.user_conf, 'r',
+ encoding='utf-8')
+
+ def test_non_existing_config_dir(self):
+ """
+ Run test mode and pass a non-existing configuration directory
+ """
+ args = ['--conf', self.default_config_file, 'test', 'foo']
+ jenkins_jobs = entry.JenkinsJobs(args)
+ self.assertRaises(IOError, jenkins_jobs.execute)
+
+ def test_non_existing_config_file(self):
+ """
+ Run test mode and pass a non-existing configuration file
+ """
+ args = ['--conf', self.default_config_file, 'test',
+ 'non-existing.yaml']
+ jenkins_jobs = entry.JenkinsJobs(args)
+ self.assertRaises(IOError, jenkins_jobs.execute)
diff --git a/tox.ini b/tox.ini
index 3bd156660..e2924ffed 100644
--- a/tox.ini
+++ b/tox.ini
@@ -49,4 +49,4 @@ commands = {posargs}
# don't submit patches that solely correct them or enable them.
ignore = E125,H
show-source = True
-exclude = .venv,.tox,dist,doc,build,*.egg,.test
+exclude = .virtualenv,.venv,.tox,dist,doc,build,*.egg,.test