Auto-generated output from python-black

Please review the following patch containing the code changes in
the repo. This patch is a transition patch and is the auto-generated
output of the python-black tool.

Change-Id: I2d2de71da8a105fb62b561899ae78441ddab4032
Signed-off-by: Thanh Ha <zxiiro@gmail.com>
This commit is contained in:
Thanh Ha 2019-08-23 09:14:39 -04:00 committed by Sorin Sbarnea
parent ead185134d
commit 4d90c187a9
89 changed files with 10446 additions and 9903 deletions

View File

@ -18,8 +18,8 @@ from jenkins_jobs.version import version_info as jenkins_jobs_version
# If extensions (or modules to document with autodoc) are in another directory, # If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the # add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('../..')) sys.path.insert(0, os.path.abspath("../.."))
sys.path.insert(0, os.path.abspath('../../jenkins_jobs/modules')) sys.path.insert(0, os.path.abspath("../../jenkins_jobs/modules"))
# -- General configuration ---------------------------------------------------- # -- General configuration ----------------------------------------------------
@ -28,25 +28,30 @@ sys.path.insert(0, os.path.abspath('../../jenkins_jobs/modules'))
# Add any Sphinx extension module names here, as strings. They can be # Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', extensions = [
'jenkins_jobs.sphinx.yaml', 'sphinxcontrib.programoutput', "sphinx.ext.autodoc",
'sphinx.ext.extlinks', 'sphinx.ext.doctest'] "sphinx.ext.coverage",
"jenkins_jobs.sphinx.yaml",
"sphinxcontrib.programoutput",
"sphinx.ext.extlinks",
"sphinx.ext.doctest",
]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ["_templates"]
# The suffix of source filenames. # The suffix of source filenames.
source_suffix = '.rst' source_suffix = ".rst"
# The encoding of source files. # The encoding of source files.
# source_encoding = 'utf-8-sig' # source_encoding = 'utf-8-sig'
# The master toctree document. # The master toctree document.
master_doc = 'index' master_doc = "index"
# General information about the project. # General information about the project.
project = u'Jenkins Job Builder' project = u"Jenkins Job Builder"
copyright = u'2012, Jenkins Job Builder Maintainers' copyright = u"2012, Jenkins Job Builder Maintainers"
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
@ -86,7 +91,7 @@ exclude_patterns = []
# show_authors = False # show_authors = False
# The name of the Pygments (syntax highlighting) style to use. # The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx' pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting. # A list of ignored prefixes for module index sorting.
# modindex_common_prefix = [] # modindex_common_prefix = []
@ -96,7 +101,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # a list of builtin themes.
html_theme = 'default' html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme # Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the # further. For a list of options available for each theme, see the
@ -169,7 +174,7 @@ html_theme = 'default'
# html_file_suffix = None # html_file_suffix = None
# Output file base name for HTML help builder. # Output file base name for HTML help builder.
htmlhelp_basename = 'JenkinsJobBuilderdoc' htmlhelp_basename = "JenkinsJobBuilderdoc"
# -- Options for LaTeX output ------------------------------------------------- # -- Options for LaTeX output -------------------------------------------------
@ -177,10 +182,8 @@ htmlhelp_basename = 'JenkinsJobBuilderdoc'
latex_elements = { latex_elements = {
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper', # 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt'). # The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt', # 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble. # Additional stuff for the LaTeX preamble.
# 'preamble': '', # 'preamble': '',
} }
@ -189,8 +192,13 @@ latex_elements = {
# (source start file, target name, title, author, documentclass # (source start file, target name, title, author, documentclass
# [howto/manual]). # [howto/manual]).
latex_documents = [ latex_documents = [
('index', 'JenkinsJobBuilder.tex', u'Jenkins Job Builder Documentation', (
u'Jenkins Job Builder Maintainers', 'manual'), "index",
"JenkinsJobBuilder.tex",
u"Jenkins Job Builder Documentation",
u"Jenkins Job Builder Maintainers",
"manual",
)
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
@ -225,8 +233,13 @@ linkcheck_timeout = 15
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [
('index', 'jenkins-jobs', u'Jenkins Job Builder Documentation', (
[u'Jenkins Job Builder Maintainers'], 1) "index",
"jenkins-jobs",
u"Jenkins Job Builder Documentation",
[u"Jenkins Job Builder Maintainers"],
1,
)
] ]
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
@ -239,10 +252,15 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
('index', 'JenkinsJobBuilder', u'Jenkins Job Builder Documentation', (
u'Jenkins Job Builder Maintainers', "index",
'JenkinsJobBuilder', 'One line description of project.', "JenkinsJobBuilder",
'Miscellaneous'), u"Jenkins Job Builder Documentation",
u"Jenkins Job Builder Maintainers",
"JenkinsJobBuilder",
"One line description of project.",
"Miscellaneous",
)
] ]
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.
@ -254,6 +272,7 @@ texinfo_documents = [
# How to display URL addresses: 'footnote', 'no', or 'inline'. # How to display URL addresses: 'footnote', 'no', or 'inline'.
# texinfo_show_urls = 'footnote' # texinfo_show_urls = 'footnote'
extlinks = {'jenkins-wiki': ('https://wiki.jenkins.io/display/JENKINS/%s', extlinks = {
None), "jenkins-wiki": ("https://wiki.jenkins.io/display/JENKINS/%s", None),
'jenkins-plugins': ('https://plugins.jenkins.io/%s', None)} "jenkins-plugins": ("https://plugins.jenkins.io/%s", None),
}

View File

@ -42,9 +42,9 @@ def getchunk(item):
# Subtract the matched portion from the original string # Subtract the matched portion from the original string
# if there was a match, otherwise set it to "" # if there was a match, otherwise set it to ""
item = (item[itemchunk.end():] if itemchunk else "") item = item[itemchunk.end() :] if itemchunk else ""
# Don't return the match object, just the text # Don't return the match object, just the text
itemchunk = (itemchunk.group() if itemchunk else "") itemchunk = itemchunk.group() if itemchunk else ""
return (itemchunk, item) return (itemchunk, item)
@ -54,28 +54,28 @@ def cmp(a, b):
def alphanum(a, b): def alphanum(a, b):
a = a.name if hasattr(a, 'name') else str(a) a = a.name if hasattr(a, "name") else str(a)
b = b.name if hasattr(b, 'name') else str(b) b = b.name if hasattr(b, "name") else str(b)
n = 0 n = 0
while (n == 0): while n == 0:
# Get a chunk and the original string with the chunk subtracted # Get a chunk and the original string with the chunk subtracted
(ac, a) = getchunk(a) (ac, a) = getchunk(a)
(bc, b) = getchunk(b) (bc, b) = getchunk(b)
# Both items contain only letters # Both items contain only letters
if (re_letters.match(ac) and re_letters.match(bc)): if re_letters.match(ac) and re_letters.match(bc):
n = cmp(ac, bc) n = cmp(ac, bc)
else: else:
# Both items contain only numbers # Both items contain only numbers
if (re_numbers.match(ac) and re_numbers.match(bc)): if re_numbers.match(ac) and re_numbers.match(bc):
n = cmp(int(ac), int(bc)) n = cmp(int(ac), int(bc))
# item has letters and one item has numbers, or one item is empty # item has letters and one item has numbers, or one item is empty
else: else:
n = cmp(ac, bc) n = cmp(ac, bc)
# Prevent deadlocks # Prevent deadlocks
if (n == 0): if n == 0:
n = 1 n = 1
return n return n
@ -105,5 +105,5 @@ class AlphanumSort(object):
if __name__ == "__main__": if __name__ == "__main__":
mylist = ['a2', 'a1', 'a10', 'a'] mylist = ["a2", "a1", "a10", "a"]
assert sorted(mylist, key=AlphanumSort) == ['a', 'a1', 'a2', 'a10'] assert sorted(mylist, key=AlphanumSort) == ["a", "a1", "a2", "a10"]

View File

@ -34,9 +34,7 @@ from jenkins_jobs.constants import MAGIC_MANAGE_STRING
from jenkins_jobs.parallel import concurrent from jenkins_jobs.parallel import concurrent
from jenkins_jobs import utils from jenkins_jobs import utils
__all__ = [ __all__ = ["JenkinsManager"]
"JenkinsManager"
]
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -44,22 +42,22 @@ _DEFAULT_TIMEOUT = object()
class JenkinsManager(object): class JenkinsManager(object):
def __init__(self, jjb_config): def __init__(self, jjb_config):
url = jjb_config.jenkins['url'] url = jjb_config.jenkins["url"]
user = jjb_config.jenkins['user'] user = jjb_config.jenkins["user"]
password = jjb_config.jenkins['password'] password = jjb_config.jenkins["password"]
timeout = jjb_config.jenkins['timeout'] timeout = jjb_config.jenkins["timeout"]
if timeout != _DEFAULT_TIMEOUT: if timeout != _DEFAULT_TIMEOUT:
self.jenkins = jenkins.Jenkins(url, user, password, timeout) self.jenkins = jenkins.Jenkins(url, user, password, timeout)
else: else:
self.jenkins = jenkins.Jenkins(url, user, password) self.jenkins = jenkins.Jenkins(url, user, password)
self.cache = JobCache(jjb_config.jenkins['url'], self.cache = JobCache(
flush=jjb_config.builder['flush_cache']) jjb_config.jenkins["url"], flush=jjb_config.builder["flush_cache"]
)
self._plugins_list = jjb_config.builder['plugins_info'] self._plugins_list = jjb_config.builder["plugins_info"]
self._jobs = None self._jobs = None
self._job_list = None self._job_list = None
self._views = None self._views = None
@ -69,16 +67,15 @@ class JenkinsManager(object):
def _setup_output(self, output, item, config_xml=False): def _setup_output(self, output, item, config_xml=False):
output_dir = output output_dir = output
output_fn = os.path.join(output, item) output_fn = os.path.join(output, item)
if '/' in item: if "/" in item:
# in item folder # in item folder
output_fn = os.path.join(output, os.path.normpath(item)) output_fn = os.path.join(output, os.path.normpath(item))
output_dir = os.path.dirname(output_fn) output_dir = os.path.dirname(output_fn)
# if in a folder, re-adding name to the directory here # if in a folder, re-adding name to the directory here
if config_xml: if config_xml:
output_dir = os.path.join( output_dir = os.path.join(output_dir, os.path.basename(item))
output_dir, os.path.basename(item)) output_fn = os.path.join(output_dir, "config.xml")
output_fn = os.path.join(output_dir, 'config.xml')
if output_dir != output: if output_dir != output:
logger.debug("Creating directory %s" % output_dir) logger.debug("Creating directory %s" % output_dir)
@ -102,36 +99,43 @@ class JenkinsManager(object):
def job_list(self): def job_list(self):
if self._job_list is None: if self._job_list is None:
# python-jenkins uses 'fullname' for folder/name combination # python-jenkins uses 'fullname' for folder/name combination
self._job_list = set(job['fullname'] for job in self.jobs) self._job_list = set(job["fullname"] for job in self.jobs)
return self._job_list return self._job_list
def _job_format(self, job_name): def _job_format(self, job_name):
# returns job name or url based on config option # returns job name or url based on config option
if self._jjb_config.builder['print_job_urls']: if self._jjb_config.builder["print_job_urls"]:
return self._jjb_config.jenkins['url'] + \ return (
'/job/' + quote( self._jjb_config.jenkins["url"]
'/job/'.join(job_name.split('/')).encode('utf8')) + '/' + "/job/"
+ quote("/job/".join(job_name.split("/")).encode("utf8"))
+ "/"
)
else: else:
return job_name return job_name
def _view_format(self, view_name): def _view_format(self, view_name):
# returns job name or url based on config option # returns job name or url based on config option
if self._jjb_config.builder['print_job_urls']: if self._jjb_config.builder["print_job_urls"]:
parts = view_name.split('/') parts = view_name.split("/")
return self._jjb_config.jenkins['url'] + \ return (
''.join(['/job/' + item for item in parts[:-1]]) + \ self._jjb_config.jenkins["url"]
'/view/' + parts[-1] + '/' + "".join(["/job/" + item for item in parts[:-1]])
+ "/view/"
+ parts[-1]
+ "/"
)
else: else:
return view_name return view_name
def update_job(self, job_name, xml): def update_job(self, job_name, xml):
if self.is_job(job_name): if self.is_job(job_name):
logger.info("Reconfiguring jenkins job {0}".format( logger.info(
self._job_format(job_name))) "Reconfiguring jenkins job {0}".format(self._job_format(job_name))
)
self.jenkins.reconfig_job(job_name, xml) self.jenkins.reconfig_job(job_name, xml)
else: else:
logger.info("Creating jenkins job {0}".format( logger.info("Creating jenkins job {0}".format(self._job_format(job_name)))
self._job_format(job_name)))
self.jenkins.create_job(job_name, xml) self.jenkins.create_job(job_name, xml)
def is_job(self, job_name, use_cache=True): def is_job(self, job_name, use_cache=True):
@ -143,7 +147,7 @@ class JenkinsManager(object):
def get_job_md5(self, job_name): def get_job_md5(self, job_name):
xml = self.jenkins.get_job_config(job_name) xml = self.jenkins.get_job_config(job_name)
return hashlib.md5(xml.encode('utf-8')).hexdigest() return hashlib.md5(xml.encode("utf-8")).hexdigest()
def delete_job(self, job_name): def delete_job(self, job_name):
if self.is_job(job_name): if self.is_job(job_name):
@ -162,10 +166,10 @@ class JenkinsManager(object):
logger.warning( logger.warning(
"Unable to retrieve Jenkins Plugin Info from {0}," "Unable to retrieve Jenkins Plugin Info from {0},"
" using default empty plugins info list.".format( " using default empty plugins info list.".format(
self.jenkins.server)) self.jenkins.server
plugins_list = [{'shortName': '', )
'version': '', )
'longName': ''}] plugins_list = [{"shortName": "", "version": "", "longName": ""}]
else: else:
raise raise
logger.debug("Jenkins Plugin Info {0}".format(pformat(plugins_list))) logger.debug("Jenkins Plugin Info {0}".format(pformat(plugins_list)))
@ -181,7 +185,7 @@ class JenkinsManager(object):
def is_managed(self, job_name): def is_managed(self, job_name):
xml = self.jenkins.get_job_config(job_name) xml = self.jenkins.get_job_config(job_name)
try: try:
out = XML.fromstring(xml.encode('utf-8')) out = XML.fromstring(xml.encode("utf-8"))
description = out.find(".//description").text description = out.find(".//description").text
return description.endswith(MAGIC_MANAGE_STRING) return description.endswith(MAGIC_MANAGE_STRING)
except (TypeError, AttributeError): except (TypeError, AttributeError):
@ -202,18 +206,21 @@ class JenkinsManager(object):
for job in jobs: for job in jobs:
# python-jenkins stores the folder and name as 'fullname' # python-jenkins stores the folder and name as 'fullname'
# Check if the job was deleted when his parent folder was deleted # Check if the job was deleted when his parent folder was deleted
if job['fullname'] not in keep and \ if job["fullname"] not in keep and self.is_job(
self.is_job(job['fullname'], use_cache=False): job["fullname"], use_cache=False
if self.is_managed(job['fullname']): ):
logger.info("Removing obsolete jenkins job {0}" if self.is_managed(job["fullname"]):
.format(job['fullname'])) logger.info(
self.delete_job(job['fullname']) "Removing obsolete jenkins job {0}".format(job["fullname"])
)
self.delete_job(job["fullname"])
deleted_jobs += 1 deleted_jobs += 1
else: else:
logger.info("Not deleting unmanaged jenkins job %s", logger.info(
job['fullname']) "Not deleting unmanaged jenkins job %s", job["fullname"]
)
else: else:
logger.debug("Keeping job %s", job['fullname']) logger.debug("Keeping job %s", job["fullname"])
return deleted_jobs return deleted_jobs
def delete_jobs(self, jobs): def delete_jobs(self, jobs):
@ -221,15 +228,17 @@ class JenkinsManager(object):
logger.info("Removing jenkins job(s): %s" % ", ".join(jobs)) logger.info("Removing jenkins job(s): %s" % ", ".join(jobs))
for job in jobs: for job in jobs:
self.delete_job(job) self.delete_job(job)
if(self.cache.is_cached(job)): if self.cache.is_cached(job):
self.cache.set(job, '') self.cache.set(job, "")
self.cache.save() self.cache.save()
def delete_all_jobs(self): def delete_all_jobs(self):
jobs = self.get_jobs() jobs = self.get_jobs()
logger.info("Number of jobs to delete: %d", len(jobs)) logger.info("Number of jobs to delete: %d", len(jobs))
script = ('for(job in jenkins.model.Jenkins.theInstance.getAllItems())' script = (
' { job.delete(); }') "for(job in jenkins.model.Jenkins.theInstance.getAllItems())"
" { job.delete(); }"
)
self.jenkins.run_script(script) self.jenkins.run_script(script)
# Need to clear the JJB cache after deletion # Need to clear the JJB cache after deletion
self.cache.clear() self.cache.clear()
@ -237,8 +246,9 @@ class JenkinsManager(object):
def changed(self, job): def changed(self, job):
md5 = job.md5() md5 = job.md5()
changed = (self._jjb_config.builder['ignore_cache'] or changed = self._jjb_config.builder["ignore_cache"] or self.cache.has_changed(
self.cache.has_changed(job.name, md5)) job.name, md5
)
if not changed: if not changed:
logger.debug("'{0}' has not changed".format(job.name)) logger.debug("'{0}' has not changed".format(job.name))
return changed return changed
@ -249,15 +259,20 @@ class JenkinsManager(object):
logger.debug("'{0}' does not currently exist".format(job.name)) logger.debug("'{0}' does not currently exist".format(job.name))
return exists return exists
def update_jobs(self, xml_jobs, output=None, n_workers=None, def update_jobs(
existing_only=None, config_xml=False): self,
xml_jobs,
output=None,
n_workers=None,
existing_only=None,
config_xml=False,
):
orig = time.time() orig = time.time()
logger.info("Number of jobs generated: %d", len(xml_jobs)) logger.info("Number of jobs generated: %d", len(xml_jobs))
xml_jobs.sort(key=AlphanumSort) xml_jobs.sort(key=AlphanumSort)
if (output and not hasattr(output, 'write') and if output and not hasattr(output, "write") and not os.path.isdir(output):
not os.path.isdir(output)):
logger.debug("Creating directory %s" % output) logger.debug("Creating directory %s" % output)
try: try:
os.makedirs(output) os.makedirs(output)
@ -267,11 +282,11 @@ class JenkinsManager(object):
if output: if output:
# ensure only wrapped once # ensure only wrapped once
if hasattr(output, 'write'): if hasattr(output, "write"):
output = utils.wrap_stream(output) output = utils.wrap_stream(output)
for job in xml_jobs: for job in xml_jobs:
if hasattr(output, 'write'): if hasattr(output, "write"):
# `output` is a file-like object # `output` is a file-like object
logger.info("Job name: %s", job.name) logger.info("Job name: %s", job.name)
logger.debug("Writing XML to '{0}'".format(output)) logger.debug("Writing XML to '{0}'".format(output))
@ -289,39 +304,31 @@ class JenkinsManager(object):
output_fn = self._setup_output(output, job.name, config_xml) output_fn = self._setup_output(output, job.name, config_xml)
logger.debug("Writing XML to '{0}'".format(output_fn)) logger.debug("Writing XML to '{0}'".format(output_fn))
with io.open(output_fn, 'w', encoding='utf-8') as f: with io.open(output_fn, "w", encoding="utf-8") as f:
f.write(job.output().decode('utf-8')) f.write(job.output().decode("utf-8"))
return xml_jobs, len(xml_jobs) return xml_jobs, len(xml_jobs)
# Filter out the jobs that did not change # Filter out the jobs that did not change
logging.debug('Filtering %d jobs for changed jobs', logging.debug("Filtering %d jobs for changed jobs", len(xml_jobs))
len(xml_jobs))
step = time.time() step = time.time()
jobs = [job for job in xml_jobs jobs = [job for job in xml_jobs if self.changed(job)]
if self.changed(job)] logging.debug("Filtered for changed jobs in %ss", (time.time() - step))
logging.debug("Filtered for changed jobs in %ss",
(time.time() - step))
if existing_only: if existing_only:
# Filter out the jobs not already in the cache # Filter out the jobs not already in the cache
logging.debug('Filtering %d jobs for existing jobs', logging.debug("Filtering %d jobs for existing jobs", len(jobs))
len(jobs))
step = time.time() step = time.time()
jobs = [job for job in jobs jobs = [job for job in jobs if self.exists(job)]
if self.exists(job)] logging.debug("Filtered for existing jobs in %ss", (time.time() - step))
logging.debug("Filtered for existing jobs in %ss",
(time.time() - step))
if not jobs: if not jobs:
return [], 0 return [], 0
# Update the jobs # Update the jobs
logging.debug('Updating jobs') logging.debug("Updating jobs")
step = time.time() step = time.time()
p_params = [{'job': job} for job in jobs] p_params = [{"job": job} for job in jobs]
results = self.parallel_update_job( results = self.parallel_update_job(n_workers=n_workers, concurrent=p_params)
n_workers=n_workers,
concurrent=p_params)
logging.debug("Parsing results") logging.debug("Parsing results")
# generalize the result parsing, as a concurrent job always returns a # generalize the result parsing, as a concurrent job always returns a
# list # list
@ -336,15 +343,13 @@ class JenkinsManager(object):
self.cache.set(j_name, j_md5) self.cache.set(j_name, j_md5)
# write cache to disk # write cache to disk
self.cache.save() self.cache.save()
logging.debug("Updated %d jobs in %ss", logging.debug("Updated %d jobs in %ss", len(jobs), time.time() - step)
len(jobs),
time.time() - step)
logging.debug("Total run took %ss", (time.time() - orig)) logging.debug("Total run took %ss", (time.time() - orig))
return jobs, len(jobs) return jobs, len(jobs)
@concurrent @concurrent
def parallel_update_job(self, job): def parallel_update_job(self, job):
self.update_job(job.name, job.output().decode('utf-8')) self.update_job(job.name, job.output().decode("utf-8"))
return (job.name, job.md5()) return (job.name, job.md5())
################ ################
@ -361,7 +366,7 @@ class JenkinsManager(object):
@property @property
def view_list(self): def view_list(self):
if self._view_list is None: if self._view_list is None:
self._view_list = set(view['name'] for view in self.views) self._view_list = set(view["name"] for view in self.views)
return self._view_list return self._view_list
def get_views(self, cache=True): def get_views(self, cache=True):
@ -389,7 +394,7 @@ class JenkinsManager(object):
for view in views: for view in views:
self.delete_view(view) self.delete_view(view)
if self.cache.is_cached(view): if self.cache.is_cached(view):
self.cache.set(view, '') self.cache.set(view, "")
self.cache.save() self.cache.save()
def delete_all_views(self): def delete_all_views(self):
@ -399,22 +404,30 @@ class JenkinsManager(object):
views.pop(0) views.pop(0)
logger.info("Number of views to delete: %d", len(views)) logger.info("Number of views to delete: %d", len(views))
for view in views: for view in views:
self.delete_view(view['name']) self.delete_view(view["name"])
# Need to clear the JJB cache after deletion # Need to clear the JJB cache after deletion
self.cache.clear() self.cache.clear()
def update_view(self, view_name, xml): def update_view(self, view_name, xml):
if self.is_view(view_name): if self.is_view(view_name):
logger.info("Reconfiguring jenkins view {0}".format( logger.info(
self._view_format(view_name))) "Reconfiguring jenkins view {0}".format(self._view_format(view_name))
)
self.jenkins.reconfig_view(view_name, xml) self.jenkins.reconfig_view(view_name, xml)
else: else:
logger.info("Creating jenkins view {0}".format( logger.info(
self._view_format(view_name))) "Creating jenkins view {0}".format(self._view_format(view_name))
)
self.jenkins.create_view(view_name, xml) self.jenkins.create_view(view_name, xml)
def update_views(self, xml_views, output=None, n_workers=None, def update_views(
existing_only=None, config_xml=False): self,
xml_views,
output=None,
n_workers=None,
existing_only=None,
config_xml=False,
):
orig = time.time() orig = time.time()
logger.info("Number of views generated: %d", len(xml_views)) logger.info("Number of views generated: %d", len(xml_views))
@ -422,11 +435,11 @@ class JenkinsManager(object):
if output: if output:
# ensure only wrapped once # ensure only wrapped once
if hasattr(output, 'write'): if hasattr(output, "write"):
output = utils.wrap_stream(output) output = utils.wrap_stream(output)
for view in xml_views: for view in xml_views:
if hasattr(output, 'write'): if hasattr(output, "write"):
# `output` is a file-like object # `output` is a file-like object
logger.info("View name: %s", view.name) logger.info("View name: %s", view.name)
logger.debug("Writing XML to '{0}'".format(output)) logger.debug("Writing XML to '{0}'".format(output))
@ -444,39 +457,31 @@ class JenkinsManager(object):
output_fn = self._setup_output(output, view.name, config_xml) output_fn = self._setup_output(output, view.name, config_xml)
logger.debug("Writing XML to '{0}'".format(output_fn)) logger.debug("Writing XML to '{0}'".format(output_fn))
with io.open(output_fn, 'w', encoding='utf-8') as f: with io.open(output_fn, "w", encoding="utf-8") as f:
f.write(view.output().decode('utf-8')) f.write(view.output().decode("utf-8"))
return xml_views, len(xml_views) return xml_views, len(xml_views)
# Filter out the views that did not change # Filter out the views that did not change
logging.debug('Filtering %d views for changed views', logging.debug("Filtering %d views for changed views", len(xml_views))
len(xml_views))
step = time.time() step = time.time()
views = [view for view in xml_views views = [view for view in xml_views if self.changed(view)]
if self.changed(view)] logging.debug("Filtered for changed views in %ss", (time.time() - step))
logging.debug("Filtered for changed views in %ss",
(time.time() - step))
if existing_only: if existing_only:
# Filter out the jobs not already in the cache # Filter out the jobs not already in the cache
logging.debug('Filtering %d views for existing jobs', logging.debug("Filtering %d views for existing jobs", len(views))
len(views))
step = time.time() step = time.time()
views = [view for view in views views = [view for view in views if self.exists(view)]
if self.exists(view)] logging.debug("Filtered for existing views in %ss", (time.time() - step))
logging.debug("Filtered for existing views in %ss",
(time.time() - step))
if not views: if not views:
return [], 0 return [], 0
# Update the views # Update the views
logging.debug('Updating views') logging.debug("Updating views")
step = time.time() step = time.time()
p_params = [{'view': view} for view in views] p_params = [{"view": view} for view in views]
results = self.parallel_update_view( results = self.parallel_update_view(n_workers=n_workers, concurrent=p_params)
n_workers=n_workers,
concurrent=p_params)
logging.debug("Parsing results") logging.debug("Parsing results")
# generalize the result parsing, as a concurrent view always returns a # generalize the result parsing, as a concurrent view always returns a
# list # list
@ -491,13 +496,11 @@ class JenkinsManager(object):
self.cache.set(v_name, v_md5) self.cache.set(v_name, v_md5)
# write cache to disk # write cache to disk
self.cache.save() self.cache.save()
logging.debug("Updated %d views in %ss", logging.debug("Updated %d views in %ss", len(views), time.time() - step)
len(views),
time.time() - step)
logging.debug("Total run took %ss", (time.time() - orig)) logging.debug("Total run took %ss", (time.time() - orig))
return views, len(views) return views, len(views)
@concurrent @concurrent
def parallel_update_view(self, view): def parallel_update_view(self, view):
self.update_view(view.name, view.output().decode('utf-8')) self.update_view(view.name, view.output().decode("utf-8"))
return (view.name, view.md5()) return (view.name, view.md5())

View File

@ -43,43 +43,45 @@ class JobCache(object):
def __init__(self, jenkins_url, flush=False): def __init__(self, jenkins_url, flush=False):
cache_dir = self.get_cache_dir() cache_dir = self.get_cache_dir()
# One cache per remote Jenkins URL: # One cache per remote Jenkins URL:
host_vary = re.sub(r'[^A-Za-z0-9\-\~]', '_', jenkins_url) host_vary = re.sub(r"[^A-Za-z0-9\-\~]", "_", jenkins_url)
self.cachefilename = os.path.join( self.cachefilename = os.path.join(
cache_dir, 'cache-host-jobs-' + host_vary + '.yml') cache_dir, "cache-host-jobs-" + host_vary + ".yml"
)
# generate named lockfile if none exists, and lock it # generate named lockfile if none exists, and lock it
self._locked = self._lock() self._locked = self._lock()
if not self._locked: if not self._locked:
raise errors.JenkinsJobsException( raise errors.JenkinsJobsException(
"Unable to lock cache for '%s'" % jenkins_url) "Unable to lock cache for '%s'" % jenkins_url
)
if flush or not os.path.isfile(self.cachefilename): if flush or not os.path.isfile(self.cachefilename):
self.data = {} self.data = {}
else: else:
with io.open(self.cachefilename, 'r', encoding='utf-8') as yfile: with io.open(self.cachefilename, "r", encoding="utf-8") as yfile:
self.data = yaml.load(yfile) self.data = yaml.load(yfile)
logger.debug("Using cache: '{0}'".format(self.cachefilename)) logger.debug("Using cache: '{0}'".format(self.cachefilename))
def _lock(self): def _lock(self):
self._fastener = fasteners.InterProcessLock("%s.lock" % self._fastener = fasteners.InterProcessLock("%s.lock" % self.cachefilename)
self.cachefilename)
return self._fastener.acquire(delay=1, max_delay=2, timeout=60) return self._fastener.acquire(delay=1, max_delay=2, timeout=60)
def _unlock(self): def _unlock(self):
if getattr(self, '_locked', False): if getattr(self, "_locked", False):
if getattr(self, '_fastener', None) is not None: if getattr(self, "_fastener", None) is not None:
self._fastener.release() self._fastener.release()
self._locked = None self._locked = None
@staticmethod @staticmethod
def get_cache_dir(): def get_cache_dir():
home = os.path.expanduser('~') home = os.path.expanduser("~")
if home == '~': if home == "~":
raise OSError('Could not locate home folder') raise OSError("Could not locate home folder")
xdg_cache_home = os.environ.get('XDG_CACHE_HOME') or \ xdg_cache_home = os.environ.get("XDG_CACHE_HOME") or os.path.join(
os.path.join(home, '.cache') home, ".cache"
path = os.path.join(xdg_cache_home, 'jenkins_jobs') )
path = os.path.join(xdg_cache_home, "jenkins_jobs")
if not os.path.isdir(path): if not os.path.isdir(path):
try: try:
os.makedirs(path) os.makedirs(path)
@ -111,9 +113,10 @@ class JobCache(object):
# use self references to required modules in case called via __del__ # use self references to required modules in case called via __del__
# write to tempfile under same directory and then replace to avoid # write to tempfile under same directory and then replace to avoid
# issues around corruption such the process be killed # issues around corruption such the process be killed
tfile = self._tempfile.NamedTemporaryFile(dir=self.get_cache_dir(), tfile = self._tempfile.NamedTemporaryFile(
delete=False) dir=self.get_cache_dir(), delete=False
tfile.write(self._yaml.dump(self.data).encode('utf-8')) )
tfile.write(self._yaml.dump(self.data).encode("utf-8"))
# force contents to be synced on disk before overwriting cachefile # force contents to be synced on disk before overwriting cachefile
tfile.flush() tfile.flush()
self._os.fsync(tfile.fileno()) self._os.fsync(tfile.fileno())
@ -131,10 +134,12 @@ class JobCache(object):
def __del__(self): def __del__(self):
# check we initialized sufficiently in case called # check we initialized sufficiently in case called
# due to an exception occurring in the __init__ # due to an exception occurring in the __init__
if getattr(self, 'data', None) is not None: if getattr(self, "data", None) is not None:
try: try:
self.save() self.save()
except Exception as e: except Exception as e:
self._logger.error("Failed to write to cache file '%s' on " self._logger.error(
"exit: %s" % (self.cachefilename, e)) "Failed to write to cache file '%s' on "
"exit: %s" % (self.cachefilename, e)
)
self._unlock() self._unlock()

View File

@ -31,8 +31,7 @@ logger = logging.getLogger()
def __version__(): def __version__():
return "Jenkins Job Builder version: %s" % \ return "Jenkins Job Builder version: %s" % version.version_info.version_string()
version.version_info.version_string()
class JenkinsJobs(object): class JenkinsJobs(object):
@ -58,17 +57,17 @@ class JenkinsJobs(object):
self.parser = create_parser() self.parser = create_parser()
self.options = self.parser.parse_args(args) self.options = self.parser.parse_args(args)
self.jjb_config = JJBConfig(self.options.conf, self.jjb_config = JJBConfig(
config_section=self.options.section, self.options.conf, config_section=self.options.section, **kwargs
**kwargs) )
if not self.options.command: if not self.options.command:
self.parser.error("Must specify a 'command' to be performed") self.parser.error("Must specify a 'command' to be performed")
if (self.options.log_level is not None): if self.options.log_level is not None:
self.options.log_level = getattr(logging, self.options.log_level = getattr(
self.options.log_level.upper(), logging, self.options.log_level.upper(), logger.getEffectiveLevel()
logger.getEffectiveLevel()) )
logger.setLevel(self.options.log_level) logger.setLevel(self.options.log_level)
self._parse_additional() self._parse_additional()
@ -84,50 +83,58 @@ class JenkinsJobs(object):
def _parse_additional(self): def _parse_additional(self):
self._set_config(self.jjb_config.builder, 'ignore_cache') self._set_config(self.jjb_config.builder, "ignore_cache")
self._set_config(self.jjb_config.builder, 'flush_cache') self._set_config(self.jjb_config.builder, "flush_cache")
self._set_config(self.jjb_config.builder, 'update') self._set_config(self.jjb_config.builder, "update")
self._set_config(self.jjb_config.yamlparser, 'allow_empty_variables') self._set_config(self.jjb_config.yamlparser, "allow_empty_variables")
self._set_config(self.jjb_config.jenkins, 'section') self._set_config(self.jjb_config.jenkins, "section")
self._set_config(self.jjb_config.jenkins, 'user') self._set_config(self.jjb_config.jenkins, "user")
self._set_config(self.jjb_config.jenkins, 'password') self._set_config(self.jjb_config.jenkins, "password")
# Note: CLI options override config file options. # Note: CLI options override config file options.
if getattr(self.options, 'update', None) is None: if getattr(self.options, "update", None) is None:
self.options.update = self.jjb_config.builder.get('update') self.options.update = self.jjb_config.builder.get("update")
if self.options.update is None: if self.options.update is None:
self.options.update = 'all' self.options.update = "all"
if getattr(self.options, 'plugins_info_path', None) is not None: if getattr(self.options, "plugins_info_path", None) is not None:
with io.open(self.options.plugins_info_path, 'r', with io.open(
encoding='utf-8') as yaml_file: self.options.plugins_info_path, "r", encoding="utf-8"
) as yaml_file:
plugins_info = yaml.load(yaml_file) plugins_info = yaml.load(yaml_file)
if not isinstance(plugins_info, list): if not isinstance(plugins_info, list):
self.parser.error("{0} must contain a Yaml list!".format( self.parser.error(
self.options.plugins_info_path)) "{0} must contain a Yaml list!".format(
self.jjb_config.builder['plugins_info'] = plugins_info self.options.plugins_info_path
)
)
self.jjb_config.builder["plugins_info"] = plugins_info
if getattr(self.options, 'path', None): if getattr(self.options, "path", None):
if hasattr(self.options.path, 'read'): if hasattr(self.options.path, "read"):
logger.debug("Input file is stdin") logger.debug("Input file is stdin")
if self.options.path.isatty(): if self.options.path.isatty():
if platform.system() == 'Windows': if platform.system() == "Windows":
key = 'CTRL+Z' key = "CTRL+Z"
else: else:
key = 'CTRL+D' key = "CTRL+D"
logger.warning("Reading configuration from STDIN. " logger.warning(
"Press %s to end input.", key) "Reading configuration from STDIN. " "Press %s to end input.",
key,
)
self.options.path = [self.options.path] self.options.path = [self.options.path]
else: else:
# take list of paths # take list of paths
self.options.path = self.options.path.split(os.pathsep) self.options.path = self.options.path.split(os.pathsep)
do_recurse = (getattr(self.options, 'recursive', False) or do_recurse = (
self.jjb_config.recursive) getattr(self.options, "recursive", False)
or self.jjb_config.recursive
)
excludes = ([e for elist in self.options.exclude excludes = [
for e in elist.split(os.pathsep)] or e for elist in self.options.exclude for e in elist.split(os.pathsep)
self.jjb_config.excludes) ] or self.jjb_config.excludes
paths = [] paths = []
for path in self.options.path: for path in self.options.path:
if do_recurse and os.path.isdir(path): if do_recurse and os.path.isdir(path):
@ -139,8 +146,8 @@ class JenkinsJobs(object):
def execute(self): def execute(self):
extension_manager = extension.ExtensionManager( extension_manager = extension.ExtensionManager(
namespace='jjb.cli.subcommands', namespace="jjb.cli.subcommands", invoke_on_load=True
invoke_on_load=True,) )
ext = extension_manager[self.options.command] ext = extension_manager[self.options.command]
ext.obj.execute(self.options, self.jjb_config) ext.obj.execute(self.options, self.jjb_config)
@ -154,10 +161,11 @@ def main():
if sys.version_info[0] == 2: if sys.version_info[0] == 2:
import codecs import codecs
reload(sys) # noqa reload(sys) # noqa
sys.setdefaultencoding('utf-8') sys.setdefaultencoding("utf-8")
sys.stdout = codecs.getwriter('utf8')(sys.stdout) sys.stdout = codecs.getwriter("utf8")(sys.stdout)
sys.stderr = codecs.getwriter('utf8')(sys.stderr) sys.stderr = codecs.getwriter("utf8")(sys.stderr)
# end of workaround # end of workaround
argv = sys.argv[1:] argv = sys.argv[1:]

View File

@ -22,8 +22,10 @@ from stevedore import extension
def __version__(): def __version__():
return "Jenkins Job Builder version: %s" % \ return (
jenkins_jobs.version.version_info.version_string() "Jenkins Job Builder version: %s"
% jenkins_jobs.version.version_info.version_string()
)
def create_parser(): def create_parser():
@ -31,67 +33,78 @@ def create_parser():
""" """
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'--conf', "--conf",
dest='conf', dest="conf",
default=os.environ.get('JJB_CONF', None), default=os.environ.get("JJB_CONF", None),
help="configuration file [JJB_CONF]") help="configuration file [JJB_CONF]",
)
parser.add_argument( parser.add_argument(
'-l', "-l",
'--log_level', "--log_level",
dest='log_level', dest="log_level",
default=os.environ.get('JJB_LOG_LEVEL', 'info'), default=os.environ.get("JJB_LOG_LEVEL", "info"),
help="log level (default: %(default)s) [JJB_LOG_LEVEL]") help="log level (default: %(default)s) [JJB_LOG_LEVEL]",
)
parser.add_argument( parser.add_argument(
'--ignore-cache', "--ignore-cache",
action='store_true', action="store_true",
dest='ignore_cache', dest="ignore_cache",
default=None, default=None,
help="ignore the cache and update the jobs anyhow (that will " help="ignore the cache and update the jobs anyhow (that will "
"only flush the specified jobs cache)") "only flush the specified jobs cache)",
)
parser.add_argument( parser.add_argument(
'--flush-cache', "--flush-cache",
action='store_true', action="store_true",
dest='flush_cache', dest="flush_cache",
default=None, default=None,
help="flush all the cache entries before updating") help="flush all the cache entries before updating",
)
parser.add_argument( parser.add_argument(
'--version', "--version",
dest='version', dest="version",
action='version', action="version",
version=__version__(), version=__version__(),
help="show version") help="show version",
)
parser.add_argument( parser.add_argument(
'--allow-empty-variables', "--allow-empty-variables",
action='store_true', action="store_true",
dest='allow_empty_variables', dest="allow_empty_variables",
default=None, default=None,
help="Don\'t fail if any of the variables inside any string are " help="Don't fail if any of the variables inside any string are "
"not defined, replace with empty string instead.") "not defined, replace with empty string instead.",
)
parser.add_argument( parser.add_argument(
'--server', '-s', "--server",
dest='section', "-s",
default=os.environ.get('JJB_SECTION', 'jenkins'), dest="section",
default=os.environ.get("JJB_SECTION", "jenkins"),
help="The Jenkins server ini section to use. Defaults to 'jenkins' " help="The Jenkins server ini section to use. Defaults to 'jenkins' "
"[JJB_SECTION]") "[JJB_SECTION]",
)
parser.add_argument( parser.add_argument(
'--user', '-u', "--user",
default=os.environ.get('JJB_USER', None), "-u",
default=os.environ.get("JJB_USER", None),
help="The Jenkins user to use for authentication. This overrides " help="The Jenkins user to use for authentication. This overrides "
"the user specified in the configuration file. [JJB_USER]") "the user specified in the configuration file. [JJB_USER]",
)
parser.add_argument( parser.add_argument(
'--password', '-p', "--password",
default=os.environ.get('JJB_PASSWORD', None), "-p",
default=os.environ.get("JJB_PASSWORD", None),
help="Password or API token to use for authenticating towards Jenkins." help="Password or API token to use for authenticating towards Jenkins."
" This overrides the password specified in the configuration file." " This overrides the password specified in the configuration file."
" [JJB_PASSWORD]") " [JJB_PASSWORD]",
)
subparser = parser.add_subparsers( subparser = parser.add_subparsers(
dest='command', dest="command", help="update, test, list or delete job"
help="update, test, list or delete job") )
extension_manager = extension.ExtensionManager( extension_manager = extension.ExtensionManager(
namespace='jjb.cli.subcommands', namespace="jjb.cli.subcommands", invoke_on_load=True
invoke_on_load=True,
) )
def parse_subcommand_args(ext, subparser): def parse_subcommand_args(ext, subparser):

View File

@ -22,6 +22,7 @@ class BaseSubCommand(object):
"""Base class for Jenkins Job Builder subcommands, intended to allow """Base class for Jenkins Job Builder subcommands, intended to allow
subcommands to be loaded as stevedore extensions by third party users. subcommands to be loaded as stevedore extensions by third party users.
""" """
def __init__(self): def __init__(self):
pass pass
@ -52,16 +53,20 @@ class BaseSubCommand(object):
"""Add '--recursive' and '--exclude' arguments to given parser. """Add '--recursive' and '--exclude' arguments to given parser.
""" """
parser.add_argument( parser.add_argument(
'-r', '--recursive', "-r",
action='store_true', "--recursive",
dest='recursive', action="store_true",
dest="recursive",
default=False, default=False,
help="look for yaml files recursively") help="look for yaml files recursively",
)
parser.add_argument( parser.add_argument(
'-x', '--exclude', "-x",
dest='exclude', "--exclude",
action='append', dest="exclude",
action="append",
default=[], default=[],
help="paths to exclude when using recursive search, " help="paths to exclude when using recursive search, "
"uses standard globbing.") "uses standard globbing.",
)

View File

@ -22,32 +22,33 @@ import jenkins_jobs.cli.subcommand.base as base
class DeleteSubCommand(base.BaseSubCommand): class DeleteSubCommand(base.BaseSubCommand):
def parse_args(self, subparser): def parse_args(self, subparser):
delete = subparser.add_parser('delete') delete = subparser.add_parser("delete")
self.parse_option_recursive_exclude(delete) self.parse_option_recursive_exclude(delete)
delete.add_argument("name", help="name of job", nargs="+")
delete.add_argument( delete.add_argument(
'name', "-p",
help='name of job', "--path",
nargs='+')
delete.add_argument(
'-p', '--path',
default=None, default=None,
help="colon-separated list of paths to YAML files " help="colon-separated list of paths to YAML files " "or directories",
"or directories")
delete.add_argument(
'-j', '--jobs-only',
action='store_true', dest='del_jobs',
default=False,
help='delete only jobs'
) )
delete.add_argument( delete.add_argument(
'-v', '--views-only', "-j",
action='store_true', dest='del_views', "--jobs-only",
action="store_true",
dest="del_jobs",
default=False, default=False,
help='delete only views' help="delete only jobs",
)
delete.add_argument(
"-v",
"--views-only",
action="store_true",
dest="del_views",
default=False,
help="delete only views",
) )
def execute(self, options, jjb_config): def execute(self, options, jjb_config):
@ -55,7 +56,8 @@ class DeleteSubCommand(base.BaseSubCommand):
if options.del_jobs and options.del_views: if options.del_jobs and options.del_views:
raise JenkinsJobsException( raise JenkinsJobsException(
'"--views-only" and "--jobs-only" cannot be used together.') '"--views-only" and "--jobs-only" cannot be used together.'
)
fn = options.path fn = options.path
registry = ModuleRegistry(jjb_config, builder.plugins_list) registry = ModuleRegistry(jjb_config, builder.plugins_list)
@ -64,8 +66,8 @@ class DeleteSubCommand(base.BaseSubCommand):
if fn: if fn:
parser.load_files(fn) parser.load_files(fn)
parser.expandYaml(registry, options.name) parser.expandYaml(registry, options.name)
jobs = [j['name'] for j in parser.jobs] jobs = [j["name"] for j in parser.jobs]
views = [v['name'] for v in parser.views] views = [v["name"] for v in parser.views]
else: else:
jobs = options.name jobs = options.name
views = options.name views = options.name

View File

@ -27,26 +27,30 @@ logger = logging.getLogger(__name__)
class DeleteAllSubCommand(base.BaseSubCommand): class DeleteAllSubCommand(base.BaseSubCommand):
def parse_args(self, subparser): def parse_args(self, subparser):
delete_all = subparser.add_parser( delete_all = subparser.add_parser(
'delete-all', "delete-all",
help="delete *ALL* jobs from Jenkins server, including " help="delete *ALL* jobs from Jenkins server, including "
"those not managed by Jenkins Job Builder.") "those not managed by Jenkins Job Builder.",
)
self.parse_option_recursive_exclude(delete_all) self.parse_option_recursive_exclude(delete_all)
delete_all.add_argument( delete_all.add_argument(
'-j', '--jobs-only', "-j",
action='store_true', dest='del_jobs', "--jobs-only",
action="store_true",
dest="del_jobs",
default=False, default=False,
help='delete only jobs' help="delete only jobs",
) )
delete_all.add_argument( delete_all.add_argument(
'-v', '--views-only', "-v",
action='store_true', dest='del_views', "--views-only",
action="store_true",
dest="del_views",
default=False, default=False,
help='delete only views' help="delete only views",
) )
def execute(self, options, jjb_config): def execute(self, options, jjb_config):
@ -55,24 +59,26 @@ class DeleteAllSubCommand(base.BaseSubCommand):
reach = set() reach = set()
if options.del_jobs and options.del_views: if options.del_jobs and options.del_views:
raise JenkinsJobsException( raise JenkinsJobsException(
'"--views-only" and "--jobs-only" cannot be used together.') '"--views-only" and "--jobs-only" cannot be used together.'
)
elif options.del_jobs and not options.del_views: elif options.del_jobs and not options.del_views:
reach.add('jobs') reach.add("jobs")
elif options.del_views and not options.del_jobs: elif options.del_views and not options.del_jobs:
reach.add('views') reach.add("views")
else: else:
reach.update(('jobs', 'views')) reach.update(("jobs", "views"))
if not utils.confirm( if not utils.confirm(
'Sure you want to delete *ALL* {} from Jenkins ' "Sure you want to delete *ALL* {} from Jenkins "
'server?\n(including those not managed by Jenkins ' "server?\n(including those not managed by Jenkins "
'Job Builder)'.format(" AND ".join(reach))): "Job Builder)".format(" AND ".join(reach))
sys.exit('Aborted') ):
sys.exit("Aborted")
if 'jobs' in reach: if "jobs" in reach:
logger.info("Deleting all jobs") logger.info("Deleting all jobs")
builder.delete_all_jobs() builder.delete_all_jobs()
if 'views' in reach: if "views" in reach:
logger.info("Deleting all views") logger.info("Deleting all views")
builder.delete_all_views() builder.delete_all_views()

View File

@ -25,17 +25,18 @@ logger = logging.getLogger(__name__)
class GetPluginsInfoSubCommand(base.BaseSubCommand): class GetPluginsInfoSubCommand(base.BaseSubCommand):
def parse_args(self, subparser): def parse_args(self, subparser):
plugins_info = subparser.add_parser( plugins_info = subparser.add_parser(
'get-plugins-info', "get-plugins-info", help="get plugins info yaml by querying Jenkins server."
help='get plugins info yaml by querying Jenkins server.') )
plugins_info.add_argument( plugins_info.add_argument(
'-o', '--output-file', "-o",
default='plugins_info.yaml', "--output-file",
dest='plugins_info_file', default="plugins_info.yaml",
help='file to save output to.') dest="plugins_info_file",
help="file to save output to.",
)
def execute(self, options, jjb_config): def execute(self, options, jjb_config):
builder = JenkinsManager(jjb_config) builder = JenkinsManager(jjb_config)
@ -43,14 +44,14 @@ class GetPluginsInfoSubCommand(base.BaseSubCommand):
plugins_info = [] plugins_info = []
for plugin in plugin_data: for plugin in plugin_data:
info = { info = {
'longName': str(plugin['longName']), "longName": str(plugin["longName"]),
'shortName': str(plugin['shortName']), "shortName": str(plugin["shortName"]),
'version': str(plugin['version']), "version": str(plugin["version"]),
} }
plugins_info.append(info) plugins_info.append(info)
if options.plugins_info_file: if options.plugins_info_file:
with open(options.plugins_info_file, 'w') as outfile: with open(options.plugins_info_file, "w") as outfile:
outfile.write(yaml.dump(plugins_info)) outfile.write(yaml.dump(plugins_info))
logger.info("Generated {} file".format(options.plugins_info_file)) logger.info("Generated {} file".format(options.plugins_info_file))
else: else:

View File

@ -27,18 +27,15 @@ def list_duplicates(seq):
class ListSubCommand(base.BaseSubCommand): class ListSubCommand(base.BaseSubCommand):
def parse_args(self, subparser): def parse_args(self, subparser):
list = subparser.add_parser('list', help="List jobs") list = subparser.add_parser("list", help="List jobs")
self.parse_option_recursive_exclude(list) self.parse_option_recursive_exclude(list)
list.add_argument('names', list.add_argument("names", help="name(s) of job(s)", nargs="*", default=None)
help='name(s) of job(s)', list.add_argument(
nargs='*', "-p", "--path", default=None, help="path to YAML file or directory"
default=None) )
list.add_argument('-p', '--path', default=None,
help='path to YAML file or directory')
def execute(self, options, jjb_config): def execute(self, options, jjb_config):
self.jjb_config = jjb_config self.jjb_config = jjb_config
@ -50,24 +47,25 @@ class ListSubCommand(base.BaseSubCommand):
stdout = utils.wrap_stream(sys.stdout) stdout = utils.wrap_stream(sys.stdout)
for job in jobs: for job in jobs:
stdout.write((job + '\n').encode('utf-8')) stdout.write((job + "\n").encode("utf-8"))
def get_jobs(self, jobs_glob=None, fn=None): def get_jobs(self, jobs_glob=None, fn=None):
if fn: if fn:
r = registry.ModuleRegistry(self.jjb_config, r = registry.ModuleRegistry(self.jjb_config, self.jenkins.plugins_list)
self.jenkins.plugins_list)
p = parser.YamlParser(self.jjb_config) p = parser.YamlParser(self.jjb_config)
p.load_files(fn) p.load_files(fn)
p.expandYaml(r, jobs_glob) p.expandYaml(r, jobs_glob)
jobs = [j['name'] for j in p.jobs] jobs = [j["name"] for j in p.jobs]
else: else:
jobs = [j['name'] for j in self.jenkins.get_jobs() jobs = [
if not jobs_glob or parser.matches(j['name'], jobs_glob)] j["name"]
for j in self.jenkins.get_jobs()
if not jobs_glob or parser.matches(j["name"], jobs_glob)
]
jobs = sorted(jobs) jobs = sorted(jobs)
for duplicate in list_duplicates(jobs): for duplicate in list_duplicates(jobs):
logging.warning("Found duplicate job name '%s', likely bug.", logging.warning("Found duplicate job name '%s', likely bug.", duplicate)
duplicate)
logging.debug("Builder.get_jobs: returning %r", jobs) logging.debug("Builder.get_jobs: returning %r", jobs)

View File

@ -23,9 +23,8 @@ logger = logging.getLogger(__name__)
class TestSubCommand(update.UpdateSubCommand): class TestSubCommand(update.UpdateSubCommand):
def parse_args(self, subparser): def parse_args(self, subparser):
test = subparser.add_parser('test') test = subparser.add_parser("test")
self.parse_option_recursive_exclude(test) self.parse_option_recursive_exclude(test)
@ -33,36 +32,46 @@ class TestSubCommand(update.UpdateSubCommand):
self.parse_arg_names(test) self.parse_arg_names(test)
test.add_argument( test.add_argument(
'--config-xml', "--config-xml",
action='store_true', action="store_true",
dest='config_xml', dest="config_xml",
default=False, default=False,
help='use alternative output file layout using config.xml files') help="use alternative output file layout using config.xml files",
)
test.add_argument( test.add_argument(
'-p', '--plugin-info', "-p",
dest='plugins_info_path', "--plugin-info",
dest="plugins_info_path",
default=None, default=None,
help='path to plugin info YAML file') help="path to plugin info YAML file",
)
test.add_argument( test.add_argument(
'-o', "-o", dest="output_dir", default=sys.stdout, help="path to output XML"
dest='output_dir', )
default=sys.stdout,
help='path to output XML')
def execute(self, options, jjb_config): def execute(self, options, jjb_config):
if not options.config_xml: if not options.config_xml:
logger.warn('(Deprecated) The default output behavior of' logger.warn(
' `jenkins-jobs test` when given the --output' "(Deprecated) The default output behavior of"
' flag will change in JJB 3.0.' " `jenkins-jobs test` when given the --output"
' Instead of writing jobs to OUTPUT/jobname;' " flag will change in JJB 3.0."
' they will be written to OUTPUT/jobname/config.xml.' " Instead of writing jobs to OUTPUT/jobname;"
' The new behavior can be enabled by the passing' " they will be written to OUTPUT/jobname/config.xml."
' `--config-xml` parameter.') " The new behavior can be enabled by the passing"
" `--config-xml` parameter."
)
builder, xml_jobs, xml_views = self._generate_xmljobs( builder, xml_jobs, xml_views = self._generate_xmljobs(options, jjb_config)
options, jjb_config)
builder.update_jobs(xml_jobs, output=options.output_dir, n_workers=1, builder.update_jobs(
config_xml=options.config_xml) xml_jobs,
builder.update_views(xml_views, output=options.output_dir, n_workers=1, output=options.output_dir,
config_xml=options.config_xml) n_workers=1,
config_xml=options.config_xml,
)
builder.update_views(
xml_views,
output=options.output_dir,
n_workers=1,
config_xml=options.config_xml,
)

View File

@ -30,22 +30,19 @@ logger = logging.getLogger(__name__)
class UpdateSubCommand(base.BaseSubCommand): class UpdateSubCommand(base.BaseSubCommand):
def parse_arg_path(self, parser): def parse_arg_path(self, parser):
parser.add_argument( parser.add_argument(
'path', "path",
nargs='?', nargs="?",
default=sys.stdin, default=sys.stdin,
help="colon-separated list of paths to YAML files " help="colon-separated list of paths to YAML files " "or directories",
"or directories") )
def parse_arg_names(self, parser): def parse_arg_names(self, parser):
parser.add_argument( parser.add_argument("names", help="name(s) of job(s)", nargs="*")
'names',
help='name(s) of job(s)', nargs='*')
def parse_args(self, subparser): def parse_args(self, subparser):
update = subparser.add_parser('update') update = subparser.add_parser("update")
self.parse_option_recursive_exclude(update) self.parse_option_recursive_exclude(update)
@ -53,51 +50,59 @@ class UpdateSubCommand(base.BaseSubCommand):
self.parse_arg_names(update) self.parse_arg_names(update)
update.add_argument( update.add_argument(
'--delete-old', "--delete-old",
action='store_true', action="store_true",
dest='delete_old', dest="delete_old",
default=False, default=False,
help='delete obsolete jobs') help="delete obsolete jobs",
)
update.add_argument( update.add_argument(
'-p', '--plugin-info', "-p",
dest='plugins_info_path', "--plugin-info",
dest="plugins_info_path",
default=None, default=None,
help='path to plugin info YAML file. Can be used to provide ' help="path to plugin info YAML file. Can be used to provide "
'previously retrieved plugins info when connecting credentials ' "previously retrieved plugins info when connecting credentials "
'don\'t have permissions to query.') "don't have permissions to query.",
)
update.add_argument( update.add_argument(
'--workers', "--workers",
type=int, type=int,
default=1, default=1,
dest='n_workers', dest="n_workers",
help="number of workers to use, 0 for autodetection and 1 " help="number of workers to use, 0 for autodetection and 1 "
"for just one worker.") "for just one worker.",
)
update.add_argument( update.add_argument(
'--existing-only', "--existing-only",
action='store_true', action="store_true",
default=False, default=False,
dest='existing_only', dest="existing_only",
help='update existing jobs only') help="update existing jobs only",
)
update_type = update.add_mutually_exclusive_group() update_type = update.add_mutually_exclusive_group()
update_type.add_argument( update_type.add_argument(
'-j', '--jobs-only', "-j",
action='store_const', "--jobs-only",
dest='update', action="store_const",
const='jobs', dest="update",
help='update only jobs') const="jobs",
help="update only jobs",
)
update_type.add_argument( update_type.add_argument(
'-v', '--views-only', "-v",
action='store_const', "--views-only",
dest='update', action="store_const",
const='views', dest="update",
help='update only views') const="views",
help="update only views",
)
def _generate_xmljobs(self, options, jjb_config=None): def _generate_xmljobs(self, options, jjb_config=None):
builder = JenkinsManager(jjb_config) builder = JenkinsManager(jjb_config)
logger.info("Updating jobs in {0} ({1})".format( logger.info("Updating jobs in {0} ({1})".format(options.path, options.names))
options.path, options.names))
orig = time.time() orig = time.time()
# Generate XML # Generate XML
@ -109,45 +114,51 @@ class UpdateSubCommand(base.BaseSubCommand):
parser.load_files(options.path) parser.load_files(options.path)
registry.set_parser_data(parser.data) registry.set_parser_data(parser.data)
job_data_list, view_data_list = parser.expandYaml( job_data_list, view_data_list = parser.expandYaml(registry, options.names)
registry, options.names)
xml_jobs = xml_job_generator.generateXML(job_data_list) xml_jobs = xml_job_generator.generateXML(job_data_list)
xml_views = xml_view_generator.generateXML(view_data_list) xml_views = xml_view_generator.generateXML(view_data_list)
jobs = parser.jobs jobs = parser.jobs
step = time.time() step = time.time()
logging.debug('%d XML files generated in %ss', logging.debug("%d XML files generated in %ss", len(jobs), str(step - orig))
len(jobs), str(step - orig))
return builder, xml_jobs, xml_views return builder, xml_jobs, xml_views
def execute(self, options, jjb_config): def execute(self, options, jjb_config):
if options.n_workers < 0: if options.n_workers < 0:
raise JenkinsJobsException( raise JenkinsJobsException(
'Number of workers must be equal or greater than 0') "Number of workers must be equal or greater than 0"
)
builder, xml_jobs, xml_views = self._generate_xmljobs( builder, xml_jobs, xml_views = self._generate_xmljobs(options, jjb_config)
options, jjb_config)
if options.update == 'jobs': if options.update == "jobs":
jobs, num_updated_jobs = builder.update_jobs( jobs, num_updated_jobs = builder.update_jobs(
xml_jobs, n_workers=options.n_workers, xml_jobs,
existing_only=options.existing_only) n_workers=options.n_workers,
existing_only=options.existing_only,
)
logger.info("Number of jobs updated: %d", num_updated_jobs) logger.info("Number of jobs updated: %d", num_updated_jobs)
elif options.update == 'views': elif options.update == "views":
views, num_updated_views = builder.update_views( views, num_updated_views = builder.update_views(
xml_views, n_workers=options.n_workers, xml_views,
existing_only=options.existing_only) n_workers=options.n_workers,
existing_only=options.existing_only,
)
logger.info("Number of views updated: %d", num_updated_views) logger.info("Number of views updated: %d", num_updated_views)
else: else:
jobs, num_updated_jobs = builder.update_jobs( jobs, num_updated_jobs = builder.update_jobs(
xml_jobs, n_workers=options.n_workers, xml_jobs,
existing_only=options.existing_only) n_workers=options.n_workers,
existing_only=options.existing_only,
)
logger.info("Number of jobs updated: %d", num_updated_jobs) logger.info("Number of jobs updated: %d", num_updated_jobs)
views, num_updated_views = builder.update_views( views, num_updated_views = builder.update_views(
xml_views, n_workers=options.n_workers, xml_views,
existing_only=options.existing_only) n_workers=options.n_workers,
existing_only=options.existing_only,
)
logger.info("Number of views updated: %d", num_updated_views) logger.info("Number of views updated: %d", num_updated_views)
keep_jobs = [job.name for job in xml_jobs] keep_jobs = [job.name for job in xml_jobs]

View File

@ -27,9 +27,7 @@ from jenkins_jobs import builder
from jenkins_jobs.errors import JJBConfigException from jenkins_jobs.errors import JJBConfigException
from jenkins_jobs.errors import JenkinsJobsException from jenkins_jobs.errors import JenkinsJobsException
__all__ = [ __all__ = ["JJBConfig"]
"JJBConfig"
]
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -50,21 +48,21 @@ url=http://localhost:8080/
query_plugins_info=False query_plugins_info=False
""" """
CONFIG_REQUIRED_MESSAGE = ("A valid configuration file is required. " CONFIG_REQUIRED_MESSAGE = (
"No configuration file passed.") "A valid configuration file is required. " "No configuration file passed."
)
DEPRECATED_PLUGIN_CONFIG_SECTION_MESSAGE = ( DEPRECATED_PLUGIN_CONFIG_SECTION_MESSAGE = (
"Defining plugin configuration using a [{plugin}] section in your config" "Defining plugin configuration using a [{plugin}] section in your config"
" file is deprecated. The recommended way to define plugins now is by" " file is deprecated. The recommended way to define plugins now is by"
" using a [plugin \"{plugin}\"] section" ' using a [plugin "{plugin}"] section'
) )
_NOTSET = object() _NOTSET = object()
class JJBConfig(object): class JJBConfig(object):
def __init__(
def __init__(self, config_filename=None, self, config_filename=None, config_file_required=False, config_section="jenkins"
config_file_required=False, ):
config_section='jenkins'):
""" """
The JJBConfig class is intended to encapsulate and resolve priority The JJBConfig class is intended to encapsulate and resolve priority
@ -93,11 +91,11 @@ class JJBConfig(object):
config_parser = self._init_defaults() config_parser = self._init_defaults()
global_conf = '/etc/jenkins_jobs/jenkins_jobs.ini' global_conf = "/etc/jenkins_jobs/jenkins_jobs.ini"
user_conf = os.path.join(os.path.expanduser('~'), '.config', user_conf = os.path.join(
'jenkins_jobs', 'jenkins_jobs.ini') os.path.expanduser("~"), ".config", "jenkins_jobs", "jenkins_jobs.ini"
local_conf = os.path.join(os.path.dirname(__file__), )
'jenkins_jobs.ini') local_conf = os.path.join(os.path.dirname(__file__), "jenkins_jobs.ini")
conf = None conf = None
if config_filename is not None: if config_filename is not None:
conf = config_filename conf = config_filename
@ -120,8 +118,10 @@ class JJBConfig(object):
if config_file_required: if config_file_required:
raise JJBConfigException(CONFIG_REQUIRED_MESSAGE) raise JJBConfigException(CONFIG_REQUIRED_MESSAGE)
else: else:
logger.warning("Config file, {0}, not found. Using " logger.warning(
"default config values.".format(conf)) "Config file, {0}, not found. Using "
"default config values.".format(conf)
)
if config_fp is not None: if config_fp is not None:
if PY2: if PY2:
@ -162,33 +162,35 @@ class JJBConfig(object):
if os.path.isfile(config_filename): if os.path.isfile(config_filename):
self.__config_file = config_filename # remember file we read from self.__config_file = config_filename # remember file we read from
logger.debug("Reading config from {0}".format(config_filename)) logger.debug("Reading config from {0}".format(config_filename))
config_fp = io.open(config_filename, 'r', encoding='utf-8') config_fp = io.open(config_filename, "r", encoding="utf-8")
else: else:
raise JJBConfigException( raise JJBConfigException(
"A valid configuration file is required. " "A valid configuration file is required. "
"\n{0} is not valid.".format(config_filename)) "\n{0} is not valid.".format(config_filename)
)
return config_fp return config_fp
def _handle_deprecated_hipchat_config(self): def _handle_deprecated_hipchat_config(self):
config = self.config_parser config = self.config_parser
if config.has_section('hipchat'): if config.has_section("hipchat"):
if config.has_section('plugin "hipchat"'): if config.has_section('plugin "hipchat"'):
logger.warning( logger.warning(
"Both [hipchat] and [plugin \"hipchat\"] sections " 'Both [hipchat] and [plugin "hipchat"] sections '
"defined, legacy [hipchat] section will be ignored." "defined, legacy [hipchat] section will be ignored."
) )
else: else:
logger.warning( logger.warning(
"[hipchat] section is deprecated and should be moved to a " "[hipchat] section is deprecated and should be moved to a "
"[plugins \"hipchat\"] section instead as the [hipchat] " '[plugins "hipchat"] section instead as the [hipchat] '
"section will be ignored in the future." "section will be ignored in the future."
) )
config.add_section('plugin "hipchat"') config.add_section('plugin "hipchat"')
for option in config.options("hipchat"): for option in config.options("hipchat"):
config.set('plugin "hipchat"', option, config.set(
config.get("hipchat", option)) 'plugin "hipchat"', option, config.get("hipchat", option)
)
config.remove_section("hipchat") config.remove_section("hipchat")
@ -197,9 +199,10 @@ class JJBConfig(object):
# interpolation to remove the need for plugins to need information # interpolation to remove the need for plugins to need information
# directly from the jenkins section within code and allow variables # directly from the jenkins section within code and allow variables
# in the config file to refer instead. # in the config file to refer instead.
if (config.has_section('plugin "hipchat"') and if config.has_section('plugin "hipchat"') and not config.has_option(
not config.has_option('plugin "hipchat"', 'url')): 'plugin "hipchat"', "url"
config.set('plugin "hipchat"', "url", config.get('jenkins', 'url')) ):
config.set('plugin "hipchat"', "url", config.get("jenkins", "url"))
def _setup(self): def _setup(self):
config = self.config_parser config = self.config_parser
@ -208,26 +211,27 @@ class JJBConfig(object):
# check the ignore_cache setting # check the ignore_cache setting
ignore_cache = False ignore_cache = False
if config.has_option(self._section, 'ignore_cache'): if config.has_option(self._section, "ignore_cache"):
logger.warning("ignore_cache option should be moved to the " logger.warning(
"[job_builder] section in the config file, the " "ignore_cache option should be moved to the "
"one specified in the [jenkins] section will be " "[job_builder] section in the config file, the "
"ignored in the future") "one specified in the [jenkins] section will be "
ignore_cache = config.getboolean(self._section, 'ignore_cache') "ignored in the future"
elif config.has_option('job_builder', 'ignore_cache'): )
ignore_cache = config.getboolean('job_builder', 'ignore_cache') ignore_cache = config.getboolean(self._section, "ignore_cache")
self.builder['ignore_cache'] = ignore_cache elif config.has_option("job_builder", "ignore_cache"):
ignore_cache = config.getboolean("job_builder", "ignore_cache")
self.builder["ignore_cache"] = ignore_cache
# check the flush_cache setting # check the flush_cache setting
flush_cache = False flush_cache = False
if config.has_option('job_builder', 'flush_cache'): if config.has_option("job_builder", "flush_cache"):
flush_cache = config.getboolean('job_builder', 'flush_cache') flush_cache = config.getboolean("job_builder", "flush_cache")
self.builder['flush_cache'] = flush_cache self.builder["flush_cache"] = flush_cache
# check the print_job_urls setting # check the print_job_urls setting
if config.has_option('job_builder', 'print_job_urls'): if config.has_option("job_builder", "print_job_urls"):
self.print_job_urls = config.getboolean('job_builder', self.print_job_urls = config.getboolean("job_builder", "print_job_urls")
'print_job_urls')
# Jenkins supports access as an anonymous user, which can be used to # Jenkins supports access as an anonymous user, which can be used to
# ensure read-only behaviour when querying the version of plugins # ensure read-only behaviour when querying the version of plugins
@ -240,16 +244,16 @@ class JJBConfig(object):
# https://bugs.launchpad.net/openstack-ci/+bug/1259631 # https://bugs.launchpad.net/openstack-ci/+bug/1259631
try: try:
user = config.get(self._section, 'user') user = config.get(self._section, "user")
except (TypeError, configparser.NoOptionError): except (TypeError, configparser.NoOptionError):
user = None user = None
self.jenkins['user'] = user self.jenkins["user"] = user
try: try:
password = config.get(self._section, 'password') password = config.get(self._section, "password")
except (TypeError, configparser.NoOptionError): except (TypeError, configparser.NoOptionError):
password = None password = None
self.jenkins['password'] = password self.jenkins["password"] = password
# None -- no timeout, blocking mode; same as setblocking(True) # None -- no timeout, blocking mode; same as setblocking(True)
# 0.0 -- non-blocking mode; same as setblocking(False) <--- default # 0.0 -- non-blocking mode; same as setblocking(False) <--- default
@ -259,86 +263,94 @@ class JJBConfig(object):
# "timeout=jenkins_jobs.builder._DEFAULT_TIMEOUT" or not set timeout at # "timeout=jenkins_jobs.builder._DEFAULT_TIMEOUT" or not set timeout at
# all. # all.
try: try:
timeout = config.getfloat(self._section, 'timeout') timeout = config.getfloat(self._section, "timeout")
except (ValueError): except (ValueError):
raise JenkinsJobsException("Jenkins timeout config is invalid") raise JenkinsJobsException("Jenkins timeout config is invalid")
except (TypeError, configparser.NoOptionError): except (TypeError, configparser.NoOptionError):
timeout = builder._DEFAULT_TIMEOUT timeout = builder._DEFAULT_TIMEOUT
self.jenkins['timeout'] = timeout self.jenkins["timeout"] = timeout
plugins_info = None plugins_info = None
if (config.has_option(self._section, 'query_plugins_info') and if config.has_option(
not config.getboolean(self._section, "query_plugins_info")): self._section, "query_plugins_info"
) and not config.getboolean(self._section, "query_plugins_info"):
logger.debug("Skipping plugin info retrieval") logger.debug("Skipping plugin info retrieval")
plugins_info = [] plugins_info = []
self.builder['plugins_info'] = plugins_info self.builder["plugins_info"] = plugins_info
self.recursive = config.getboolean('job_builder', 'recursive') self.recursive = config.getboolean("job_builder", "recursive")
self.excludes = config.get('job_builder', 'exclude').split(os.pathsep) self.excludes = config.get("job_builder", "exclude").split(os.pathsep)
# The way we want to do things moving forward: # The way we want to do things moving forward:
self.jenkins['url'] = config.get(self._section, 'url') self.jenkins["url"] = config.get(self._section, "url")
self.builder['print_job_urls'] = self.print_job_urls self.builder["print_job_urls"] = self.print_job_urls
# keep descriptions ? (used by yamlparser) # keep descriptions ? (used by yamlparser)
keep_desc = False keep_desc = False
if (config and config.has_section('job_builder') and if (
config.has_option('job_builder', 'keep_descriptions')): config
keep_desc = config.getboolean('job_builder', and config.has_section("job_builder")
'keep_descriptions') and config.has_option("job_builder", "keep_descriptions")
self.yamlparser['keep_descriptions'] = keep_desc ):
keep_desc = config.getboolean("job_builder", "keep_descriptions")
self.yamlparser["keep_descriptions"] = keep_desc
# figure out the include path (used by yamlparser) # figure out the include path (used by yamlparser)
path = ["."] path = ["."]
if (config and config.has_section('job_builder') and if (
config.has_option('job_builder', 'include_path')): config
path = config.get('job_builder', and config.has_section("job_builder")
'include_path').split(':') and config.has_option("job_builder", "include_path")
self.yamlparser['include_path'] = path ):
path = config.get("job_builder", "include_path").split(":")
self.yamlparser["include_path"] = path
# allow duplicates? # allow duplicates?
allow_duplicates = False allow_duplicates = False
if config and config.has_option('job_builder', 'allow_duplicates'): if config and config.has_option("job_builder", "allow_duplicates"):
allow_duplicates = config.getboolean('job_builder', allow_duplicates = config.getboolean("job_builder", "allow_duplicates")
'allow_duplicates') self.yamlparser["allow_duplicates"] = allow_duplicates
self.yamlparser['allow_duplicates'] = allow_duplicates
# allow empty variables? # allow empty variables?
self.yamlparser['allow_empty_variables'] = ( self.yamlparser["allow_empty_variables"] = (
config and config.has_section('job_builder') and config
config.has_option('job_builder', 'allow_empty_variables') and and config.has_section("job_builder")
config.getboolean('job_builder', 'allow_empty_variables')) and config.has_option("job_builder", "allow_empty_variables")
and config.getboolean("job_builder", "allow_empty_variables")
)
# retain anchors across files? # retain anchors across files?
retain_anchors = False retain_anchors = False
if config and config.has_option('job_builder', 'retain_anchors'): if config and config.has_option("job_builder", "retain_anchors"):
retain_anchors = config.getboolean('job_builder', retain_anchors = config.getboolean("job_builder", "retain_anchors")
'retain_anchors') self.yamlparser["retain_anchors"] = retain_anchors
self.yamlparser['retain_anchors'] = retain_anchors
update = None update = None
if (config and config.has_section('job_builder') and if (
config.has_option('job_builder', 'update')): config
update = config.get('job_builder', 'update') and config.has_section("job_builder")
self.builder['update'] = update and config.has_option("job_builder", "update")
):
update = config.get("job_builder", "update")
self.builder["update"] = update
def validate(self): def validate(self):
# Inform the user as to what is likely to happen, as they may specify # Inform the user as to what is likely to happen, as they may specify
# a real jenkins instance in test mode to get the plugin info to check # a real jenkins instance in test mode to get the plugin info to check
# the XML generated. # the XML generated.
if self.jenkins['user'] is None and self.jenkins['password'] is None: if self.jenkins["user"] is None and self.jenkins["password"] is None:
logger.info("Will use anonymous access to Jenkins if needed.") logger.info("Will use anonymous access to Jenkins if needed.")
elif ((self.jenkins['user'] is not None and elif (
self.jenkins['password'] is None) or self.jenkins["user"] is not None and self.jenkins["password"] is None
(self.jenkins['user'] is None and ) or (self.jenkins["user"] is None and self.jenkins["password"] is not None):
self.jenkins['password'] is not None)):
raise JenkinsJobsException( raise JenkinsJobsException(
"Cannot authenticate to Jenkins with only one of User and " "Cannot authenticate to Jenkins with only one of User and "
"Password provided, please check your configuration." "Password provided, please check your configuration."
) )
if (self.builder['plugins_info'] is not None and if self.builder["plugins_info"] is not None and not isinstance(
not isinstance(self.builder['plugins_info'], list)): self.builder["plugins_info"], list
):
raise JenkinsJobsException("plugins_info must contain a list!") raise JenkinsJobsException("plugins_info must contain a list!")
def get_module_config(self, section, key, default=None): def get_module_config(self, section, key, default=None):
@ -349,19 +361,23 @@ class JJBConfig(object):
""" """
result = default result = default
try: try:
result = self.config_parser.get( result = self.config_parser.get(section, key)
section, key except (
) configparser.NoSectionError,
except (configparser.NoSectionError, configparser.NoOptionError, configparser.NoOptionError,
JenkinsJobsException) as e: JenkinsJobsException,
) as e:
# use of default ignores missing sections/options # use of default ignores missing sections/options
if result is None: if result is None:
logger.warning( logger.warning(
"You didn't set a %s neither in the yaml job definition " "You didn't set a %s neither in the yaml job definition "
"nor in the %s section, blank default value will be " "nor in the %s section, blank default value will be "
"applied:\n%s", key, section, e) "applied:\n%s",
key,
section,
e,
)
return result return result
def get_plugin_config(self, plugin, key, default=None): def get_plugin_config(self, plugin, key, default=None):
return self.get_module_config('plugin "{}"'.format(plugin), key, return self.get_module_config('plugin "{}"'.format(plugin), key, default)
default)

View File

@ -4,9 +4,9 @@ import inspect
def is_sequence(arg): def is_sequence(arg):
return (not hasattr(arg, "strip") and return not hasattr(arg, "strip") and (
(hasattr(arg, "__getitem__") or hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")
hasattr(arg, "__iter__"))) )
class JenkinsJobsException(Exception): class JenkinsJobsException(Exception):
@ -14,20 +14,19 @@ class JenkinsJobsException(Exception):
class ModuleError(JenkinsJobsException): class ModuleError(JenkinsJobsException):
def get_module_name(self): def get_module_name(self):
frame = inspect.currentframe() frame = inspect.currentframe()
co_name = frame.f_code.co_name co_name = frame.f_code.co_name
module_name = '<unresolved>' module_name = "<unresolved>"
while frame and co_name != 'run': while frame and co_name != "run":
# XML generation called via dispatch # XML generation called via dispatch
if co_name == 'dispatch': if co_name == "dispatch":
data = frame.f_locals data = frame.f_locals
module_name = "%s.%s" % (data['component_type'], data['name']) module_name = "%s.%s" % (data["component_type"], data["name"])
break break
# XML generation done directly by class using gen_xml or root_xml # XML generation done directly by class using gen_xml or root_xml
if co_name == 'gen_xml' or co_name == 'root_xml': if co_name == "gen_xml" or co_name == "root_xml":
data = frame.f_locals['data'] data = frame.f_locals["data"]
module_name = next(iter(data.keys())) module_name = next(iter(data.keys()))
break break
frame = frame.f_back frame = frame.f_back
@ -37,47 +36,41 @@ class ModuleError(JenkinsJobsException):
class InvalidAttributeError(ModuleError): class InvalidAttributeError(ModuleError):
def __init__(self, attribute_name, value, valid_values=None): def __init__(self, attribute_name, value, valid_values=None):
message = "'{0}' is an invalid value for attribute {1}.{2}".format( message = "'{0}' is an invalid value for attribute {1}.{2}".format(
value, self.get_module_name(), attribute_name) value, self.get_module_name(), attribute_name
)
if is_sequence(valid_values): if is_sequence(valid_values):
message += "\nValid values include: {0}".format( message += "\nValid values include: {0}".format(
', '.join("'{0}'".format(value) ", ".join("'{0}'".format(value) for value in valid_values)
for value in valid_values)) )
super(InvalidAttributeError, self).__init__(message) super(InvalidAttributeError, self).__init__(message)
class MissingAttributeError(ModuleError): class MissingAttributeError(ModuleError):
def __init__(self, missing_attribute, module_name=None): def __init__(self, missing_attribute, module_name=None):
module = module_name or self.get_module_name() module = module_name or self.get_module_name()
if is_sequence(missing_attribute): if is_sequence(missing_attribute):
message = "One of {0} must be present in '{1}'".format( message = "One of {0} must be present in '{1}'".format(
', '.join("'{0}'".format(value) ", ".join("'{0}'".format(value) for value in missing_attribute), module
for value in missing_attribute), module) )
else: else:
message = "Missing {0} from an instance of '{1}'".format( message = "Missing {0} from an instance of '{1}'".format(
missing_attribute, module) missing_attribute, module
)
super(MissingAttributeError, self).__init__(message) super(MissingAttributeError, self).__init__(message)
class AttributeConflictError(ModuleError): class AttributeConflictError(ModuleError):
def __init__(self, attribute_name, attributes_in_conflict, module_name=None):
def __init__(
self, attribute_name, attributes_in_conflict, module_name=None
):
module = module_name or self.get_module_name() module = module_name or self.get_module_name()
message = ( message = "Attribute '{0}' can not be used together with {1} in {2}".format(
"Attribute '{0}' can not be used together with {1} in {2}".format( attribute_name,
attribute_name, ", ".join("'{0}'".format(value) for value in attributes_in_conflict),
', '.join( module,
"'{0}'".format(value) for value in attributes_in_conflict
), module
)
) )
super(AttributeConflictError, self).__init__(message) super(AttributeConflictError, self).__init__(message)

View File

@ -33,18 +33,22 @@ def deep_format(obj, paramdict, allow_empty=False):
# limitations on the values in paramdict - the post-format result must # limitations on the values in paramdict - the post-format result must
# still be valid YAML (so substituting-in a string containing quotes, for # still be valid YAML (so substituting-in a string containing quotes, for
# example, is problematic). # example, is problematic).
if hasattr(obj, 'format'): if hasattr(obj, "format"):
try: try:
ret = CustomFormatter(allow_empty).format(obj, **paramdict) ret = CustomFormatter(allow_empty).format(obj, **paramdict)
except KeyError as exc: except KeyError as exc:
missing_key = exc.args[0] missing_key = exc.args[0]
desc = "%s parameter missing to format %s\nGiven:\n%s" % ( desc = "%s parameter missing to format %s\nGiven:\n%s" % (
missing_key, obj, pformat(paramdict)) missing_key,
obj,
pformat(paramdict),
)
raise JenkinsJobsException(desc) raise JenkinsJobsException(desc)
except Exception: except Exception:
logging.error("Problem formatting with args:\nallow_empty:" logging.error(
"%s\nobj: %s\nparamdict: %s" % "Problem formatting with args:\nallow_empty:"
(allow_empty, obj, paramdict)) "%s\nobj: %s\nparamdict: %s" % (allow_empty, obj, paramdict)
)
raise raise
elif isinstance(obj, list): elif isinstance(obj, list):
@ -55,17 +59,22 @@ def deep_format(obj, paramdict, allow_empty=False):
ret = type(obj)() ret = type(obj)()
for item in obj: for item in obj:
try: try:
ret[CustomFormatter(allow_empty).format(item, **paramdict)] = \ ret[
deep_format(obj[item], paramdict, allow_empty) CustomFormatter(allow_empty).format(item, **paramdict)
] = deep_format(obj[item], paramdict, allow_empty)
except KeyError as exc: except KeyError as exc:
missing_key = exc.args[0] missing_key = exc.args[0]
desc = "%s parameter missing to format %s\nGiven:\n%s" % ( desc = "%s parameter missing to format %s\nGiven:\n%s" % (
missing_key, obj, pformat(paramdict)) missing_key,
obj,
pformat(paramdict),
)
raise JenkinsJobsException(desc) raise JenkinsJobsException(desc)
except Exception: except Exception:
logging.error("Problem formatting with args:\nallow_empty:" logging.error(
"%s\nobj: %s\nparamdict: %s" % "Problem formatting with args:\nallow_empty:"
(allow_empty, obj, paramdict)) "%s\nobj: %s\nparamdict: %s" % (allow_empty, obj, paramdict)
)
raise raise
else: else:
ret = obj ret = obj
@ -81,6 +90,7 @@ class CustomFormatter(Formatter):
Custom formatter to allow non-existing key references when formatting a Custom formatter to allow non-existing key references when formatting a
string string
""" """
_expr = r""" _expr = r"""
(?<!{){({{)* # non-pair opening { (?<!{){({{)* # non-pair opening {
(?:obj:)? # obj: (?:obj:)? # obj:
@ -99,7 +109,7 @@ class CustomFormatter(Formatter):
# special case of returning the object if the entire string # special case of returning the object if the entire string
# matches a single parameter # matches a single parameter
try: try:
result = re.match('^%s$' % self._expr, format_string, re.VERBOSE) result = re.match("^%s$" % self._expr, format_string, re.VERBOSE)
except TypeError: except TypeError:
return format_string.format(**kwargs) return format_string.format(**kwargs)
if result is not None: if result is not None:
@ -130,8 +140,7 @@ class CustomFormatter(Formatter):
except KeyError: except KeyError:
if self.allow_empty: if self.allow_empty:
logger.debug( logger.debug(
'Found uninitialized key %s, replaced with empty string', "Found uninitialized key %s, replaced with empty string", key
key
) )
return '' return ""
raise raise

View File

@ -235,9 +235,11 @@ class OrderedConstructor(BaseConstructor):
self.flatten_mapping(node) self.flatten_mapping(node)
else: else:
raise yaml.constructor.ConstructorError( raise yaml.constructor.ConstructorError(
None, None, None,
'expected a mapping node, but found %s' % node.id, None,
node.start_mark) "expected a mapping node, but found %s" % node.id,
node.start_mark,
)
mapping = OrderedDict() mapping = OrderedDict()
for key_node, value_node in node.value: for key_node, value_node in node.value:
@ -246,23 +248,26 @@ class OrderedConstructor(BaseConstructor):
hash(key) hash(key)
except TypeError as exc: except TypeError as exc:
raise yaml.constructor.ConstructorError( raise yaml.constructor.ConstructorError(
'while constructing a mapping', node.start_mark, "while constructing a mapping",
'found unacceptable key (%s)' % exc, key_node.start_mark) node.start_mark,
"found unacceptable key (%s)" % exc,
key_node.start_mark,
)
value = self.construct_object(value_node, deep=False) value = self.construct_object(value_node, deep=False)
mapping[key] = value mapping[key] = value
data.update(mapping) data.update(mapping)
class OrderedRepresenter(BaseRepresenter): class OrderedRepresenter(BaseRepresenter):
def represent_yaml_mapping(self, mapping, flow_style=None): def represent_yaml_mapping(self, mapping, flow_style=None):
tag = u'tag:yaml.org,2002:map' tag = u"tag:yaml.org,2002:map"
node = self.represent_mapping(tag, mapping, flow_style=flow_style) node = self.represent_mapping(tag, mapping, flow_style=flow_style)
return node return node
class LocalAnchorLoader(yaml.Loader): class LocalAnchorLoader(yaml.Loader):
"""Subclass for yaml.Loader which keeps Alias between calls""" """Subclass for yaml.Loader which keeps Alias between calls"""
anchors = {} anchors = {}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -319,14 +324,13 @@ class LocalLoader(OrderedConstructor, LocalAnchorLoader):
# make sure to pop off any local settings before passing to # make sure to pop off any local settings before passing to
# the parent constructor as any unknown args may cause errors. # the parent constructor as any unknown args may cause errors.
self.search_path = list() self.search_path = list()
if 'search_path' in kwargs: if "search_path" in kwargs:
for p in kwargs.pop('search_path'): for p in kwargs.pop("search_path"):
logger.debug("Adding '{0}' to search path for include tags" logger.debug("Adding '{0}' to search path for include tags".format(p))
.format(p))
self.search_path.append(os.path.normpath(p)) self.search_path.append(os.path.normpath(p))
if 'escape_callback' in kwargs: if "escape_callback" in kwargs:
self.escape_callback = kwargs.pop('escape_callback') self.escape_callback = kwargs.pop("escape_callback")
else: else:
self.escape_callback = self._escape self.escape_callback = self._escape
@ -334,16 +338,17 @@ class LocalLoader(OrderedConstructor, LocalAnchorLoader):
# constructor to preserve order of maps and ensure that the order of # constructor to preserve order of maps and ensure that the order of
# keys returned is consistent across multiple python versions # keys returned is consistent across multiple python versions
self.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, self.add_constructor(
type(self).construct_yaml_map) yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
type(self).construct_yaml_map,
)
if hasattr(self.stream, 'name'): if hasattr(self.stream, "name"):
self.search_path.append(os.path.normpath( self.search_path.append(os.path.normpath(os.path.dirname(self.stream.name)))
os.path.dirname(self.stream.name)))
self.search_path.append(os.path.normpath(os.path.curdir)) self.search_path.append(os.path.normpath(os.path.curdir))
def _escape(self, data): def _escape(self, data):
return re.sub(r'({|})', r'\1\1', data) return re.sub(r"({|})", r"\1\1", data)
class LocalDumper(OrderedRepresenter, yaml.Dumper): class LocalDumper(OrderedRepresenter, yaml.Dumper):
@ -352,12 +357,10 @@ class LocalDumper(OrderedRepresenter, yaml.Dumper):
# representer to ensure conversion back looks like normal # representer to ensure conversion back looks like normal
# mapping and hides that we use OrderedDict internally # mapping and hides that we use OrderedDict internally
self.add_representer(OrderedDict, self.add_representer(OrderedDict, type(self).represent_yaml_mapping)
type(self).represent_yaml_mapping)
# convert any tuples to lists as the JJB input is generally # convert any tuples to lists as the JJB input is generally
# in list format # in list format
self.add_representer(tuple, self.add_representer(tuple, type(self).represent_list)
type(self).represent_list)
class BaseYAMLObject(YAMLObject): class BaseYAMLObject(YAMLObject):
@ -366,7 +369,7 @@ class BaseYAMLObject(YAMLObject):
class J2String(BaseYAMLObject): class J2String(BaseYAMLObject):
yaml_tag = u'!j2:' yaml_tag = u"!j2:"
@classmethod @classmethod
def from_yaml(cls, loader, node): def from_yaml(cls, loader, node):
@ -374,7 +377,7 @@ class J2String(BaseYAMLObject):
class YamlListJoin(BaseYAMLObject): class YamlListJoin(BaseYAMLObject):
yaml_tag = u'!join:' yaml_tag = u"!join:"
@classmethod @classmethod
def from_yaml(cls, loader, node): def from_yaml(cls, loader, node):
@ -382,26 +385,34 @@ class YamlListJoin(BaseYAMLObject):
delimiter = node.value[0].value delimiter = node.value[0].value
if not isinstance(node.value[1], yaml.SequenceNode): if not isinstance(node.value[1], yaml.SequenceNode):
raise yaml.constructor.ConstructorError( raise yaml.constructor.ConstructorError(
None, None, "expected sequence node for join data, but " None,
"found %s" % node.value[1].id, node.start_mark) None,
"expected sequence node for join data, but "
"found %s" % node.value[1].id,
node.start_mark,
)
return delimiter.join((v.value for v in node.value[1].value)) return delimiter.join((v.value for v in node.value[1].value))
else: else:
raise yaml.constructor.ConstructorError( raise yaml.constructor.ConstructorError(
None, None, "expected sequence node, but found %s" % node.id, None,
node.start_mark) None,
"expected sequence node, but found %s" % node.id,
node.start_mark,
)
class YamlInclude(BaseYAMLObject): class YamlInclude(BaseYAMLObject):
yaml_tag = u'!include:' yaml_tag = u"!include:"
@classmethod @classmethod
def _find_file(cls, filename, search_path): def _find_file(cls, filename, search_path):
for dirname in search_path: for dirname in search_path:
candidate = os.path.expanduser(os.path.join(dirname, filename)) candidate = os.path.expanduser(os.path.join(dirname, filename))
if os.path.isfile(candidate): if os.path.isfile(candidate):
logger.debug("Including file '{0}' from path '{1}'" logger.debug(
.format(filename, dirname)) "Including file '{0}' from path '{1}'".format(filename, dirname)
)
return candidate return candidate
return filename return filename
@ -415,11 +426,14 @@ class YamlInclude(BaseYAMLObject):
filename = cls._find_file(node_str, loader.search_path) filename = cls._find_file(node_str, loader.search_path)
try: try:
with io.open(filename, 'r', encoding='utf-8') as f: with io.open(filename, "r", encoding="utf-8") as f:
return f.read() return f.read()
except Exception: except Exception:
logger.error("Failed to include file using search path: '{0}'" logger.error(
.format(':'.join(loader.search_path))) "Failed to include file using search path: '{0}'".format(
":".join(loader.search_path)
)
)
raise raise
@classmethod @classmethod
@ -428,15 +442,14 @@ class YamlInclude(BaseYAMLObject):
if isinstance(contents, LazyLoader): if isinstance(contents, LazyLoader):
return contents return contents
data = yaml.load(contents, data = yaml.load(
functools.partial(cls.yaml_loader, contents, functools.partial(cls.yaml_loader, search_path=loader.search_path)
search_path=loader.search_path)) )
return data return data
@classmethod @classmethod
def _lazy_load(cls, loader, tag, node_str): def _lazy_load(cls, loader, tag, node_str):
logger.info("Lazy loading of file template '{0}' enabled" logger.info("Lazy loading of file template '{0}' enabled".format(node_str))
.format(node_str))
return LazyLoader((cls, loader, node_str)) return LazyLoader((cls, loader, node_str))
@classmethod @classmethod
@ -444,20 +457,24 @@ class YamlInclude(BaseYAMLObject):
if isinstance(node, yaml.ScalarNode): if isinstance(node, yaml.ScalarNode):
return cls._from_file(loader, node) return cls._from_file(loader, node)
elif isinstance(node, yaml.SequenceNode): elif isinstance(node, yaml.SequenceNode):
contents = [cls._from_file(loader, scalar_node) contents = [
for scalar_node in node.value] cls._from_file(loader, scalar_node) for scalar_node in node.value
]
if any(isinstance(s, CustomLoader) for s in contents): if any(isinstance(s, CustomLoader) for s in contents):
return CustomLoaderCollection(contents) return CustomLoaderCollection(contents)
return u'\n'.join(contents) return u"\n".join(contents)
else: else:
raise yaml.constructor.ConstructorError( raise yaml.constructor.ConstructorError(
None, None, "expected either a sequence or scalar node, but " None,
"found %s" % node.id, node.start_mark) None,
"expected either a sequence or scalar node, but " "found %s" % node.id,
node.start_mark,
)
class YamlIncludeRaw(YamlInclude): class YamlIncludeRaw(YamlInclude):
yaml_tag = u'!include-raw:' yaml_tag = u"!include-raw:"
@classmethod @classmethod
def _from_file(cls, loader, node): def _from_file(cls, loader, node):
@ -465,23 +482,26 @@ class YamlIncludeRaw(YamlInclude):
class YamlIncludeRawEscape(YamlIncludeRaw): class YamlIncludeRawEscape(YamlIncludeRaw):
yaml_tag = u'!include-raw-escape:' yaml_tag = u"!include-raw-escape:"
@classmethod @classmethod
def from_yaml(cls, loader, node): def from_yaml(cls, loader, node):
data = YamlIncludeRaw.from_yaml(loader, node) data = YamlIncludeRaw.from_yaml(loader, node)
if isinstance(data, LazyLoader): if isinstance(data, LazyLoader):
logger.warning("Replacing %s tag with %s since lazy loading means " logger.warning(
"file contents will not be deep formatted for " "Replacing %s tag with %s since lazy loading means "
"variable substitution.", cls.yaml_tag, "file contents will not be deep formatted for "
YamlIncludeRaw.yaml_tag) "variable substitution.",
cls.yaml_tag,
YamlIncludeRaw.yaml_tag,
)
return data return data
else: else:
return loader.escape_callback(data) return loader.escape_callback(data)
class YamlIncludeJinja2(YamlIncludeRaw): class YamlIncludeJinja2(YamlIncludeRaw):
yaml_tag = u'!include-jinja2:' yaml_tag = u"!include-jinja2:"
@classmethod @classmethod
def _from_file(cls, loader, node): def _from_file(cls, loader, node):
@ -492,26 +512,28 @@ class YamlIncludeJinja2(YamlIncludeRaw):
class DeprecatedTag(BaseYAMLObject): class DeprecatedTag(BaseYAMLObject):
@classmethod @classmethod
def from_yaml(cls, loader, node): def from_yaml(cls, loader, node):
logger.warning("tag '%s' is deprecated, switch to using '%s'", logger.warning(
cls.yaml_tag, cls._new.yaml_tag) "tag '%s' is deprecated, switch to using '%s'",
cls.yaml_tag,
cls._new.yaml_tag,
)
return cls._new.from_yaml(loader, node) return cls._new.from_yaml(loader, node)
class YamlIncludeDeprecated(DeprecatedTag): class YamlIncludeDeprecated(DeprecatedTag):
yaml_tag = u'!include' yaml_tag = u"!include"
_new = YamlInclude _new = YamlInclude
class YamlIncludeRawDeprecated(DeprecatedTag): class YamlIncludeRawDeprecated(DeprecatedTag):
yaml_tag = u'!include-raw' yaml_tag = u"!include-raw"
_new = YamlIncludeRaw _new = YamlIncludeRaw
class YamlIncludeRawEscapeDeprecated(DeprecatedTag): class YamlIncludeRawEscapeDeprecated(DeprecatedTag):
yaml_tag = u'!include-raw-escape' yaml_tag = u"!include-raw-escape"
_new = YamlIncludeRawEscape _new = YamlIncludeRawEscape
@ -525,8 +547,7 @@ class Jinja2Loader(CustomLoader):
def __init__(self, contents, search_path): def __init__(self, contents, search_path):
self._template = jinja2.Template(contents) self._template = jinja2.Template(contents)
self._template.environment.undefined = jinja2.StrictUndefined self._template.environment.undefined = jinja2.StrictUndefined
self._template.environment.loader = jinja2.FileSystemLoader( self._template.environment.loader = jinja2.FileSystemLoader(search_path)
search_path)
self._loader = self._template.environment.loader self._loader = self._template.environment.loader
def format(self, **kwargs): def format(self, **kwargs):
@ -539,11 +560,12 @@ class Jinja2Loader(CustomLoader):
class CustomLoaderCollection(object): class CustomLoaderCollection(object):
"""Helper class to format a collection of CustomLoader objects""" """Helper class to format a collection of CustomLoader objects"""
def __init__(self, sequence): def __init__(self, sequence):
self._data = sequence self._data = sequence
def format(self, *args, **kwargs): def format(self, *args, **kwargs):
return u'\n'.join(item.format(*args, **kwargs) for item in self._data) return u"\n".join(item.format(*args, **kwargs) for item in self._data)
class LazyLoader(CustomLoader): class LazyLoader(CustomLoader):
@ -564,8 +586,8 @@ class LazyLoader(CustomLoader):
def format(self, *args, **kwargs): def format(self, *args, **kwargs):
node = yaml.ScalarNode( node = yaml.ScalarNode(
tag=self._node.tag, tag=self._node.tag, value=self._node.value.format(*args, **kwargs)
value=self._node.value.format(*args, **kwargs)) )
return self._cls.from_yaml(self._loader, node) return self._cls.from_yaml(self._loader, node)

File diff suppressed because it is too large Load Diff

View File

@ -123,88 +123,85 @@ class General(jenkins_jobs.modules.base.Base):
logrotate_warn_issued = False logrotate_warn_issued = False
def gen_xml(self, xml, data): def gen_xml(self, xml, data):
jdk = data.get('jdk', None) jdk = data.get("jdk", None)
if jdk: if jdk:
XML.SubElement(xml, 'jdk').text = jdk XML.SubElement(xml, "jdk").text = jdk
XML.SubElement(xml, 'actions') XML.SubElement(xml, "actions")
desc_text = data.get('description', None) desc_text = data.get("description", None)
if desc_text is not None: if desc_text is not None:
description = XML.SubElement(xml, 'description') description = XML.SubElement(xml, "description")
description.text = desc_text description.text = desc_text
XML.SubElement(xml, 'keepDependencies').text = 'false' XML.SubElement(xml, "keepDependencies").text = "false"
# Need to ensure we support the None parameter to allow disabled to # Need to ensure we support the None parameter to allow disabled to
# remain the last setting if the user purposely adds and then removes # remain the last setting if the user purposely adds and then removes
# the disabled parameter. # the disabled parameter.
# See: http://lists.openstack.org/pipermail/openstack-infra/2016-March/003980.html # noqa # See: http://lists.openstack.org/pipermail/openstack-infra/2016-March/003980.html # noqa
disabled = data.get('disabled', None) disabled = data.get("disabled", None)
if disabled is not None: if disabled is not None:
XML.SubElement(xml, 'disabled').text = str(disabled).lower() XML.SubElement(xml, "disabled").text = str(disabled).lower()
if 'display-name' in data: if "display-name" in data:
XML.SubElement(xml, 'displayName').text = data['display-name'] XML.SubElement(xml, "displayName").text = data["display-name"]
if data.get('block-downstream'): if data.get("block-downstream"):
XML.SubElement(xml, XML.SubElement(xml, "blockBuildWhenDownstreamBuilding").text = "true"
'blockBuildWhenDownstreamBuilding').text = 'true'
else: else:
XML.SubElement(xml, XML.SubElement(xml, "blockBuildWhenDownstreamBuilding").text = "false"
'blockBuildWhenDownstreamBuilding').text = 'false' if data.get("block-upstream"):
if data.get('block-upstream'): XML.SubElement(xml, "blockBuildWhenUpstreamBuilding").text = "true"
XML.SubElement(xml,
'blockBuildWhenUpstreamBuilding').text = 'true'
else: else:
XML.SubElement(xml, XML.SubElement(xml, "blockBuildWhenUpstreamBuilding").text = "false"
'blockBuildWhenUpstreamBuilding').text = 'false' authtoken = data.get("auth-token", None)
authtoken = data.get('auth-token', None)
if authtoken is not None: if authtoken is not None:
XML.SubElement(xml, 'authToken').text = authtoken XML.SubElement(xml, "authToken").text = authtoken
if data.get('concurrent'): if data.get("concurrent"):
XML.SubElement(xml, 'concurrentBuild').text = 'true' XML.SubElement(xml, "concurrentBuild").text = "true"
else: else:
XML.SubElement(xml, 'concurrentBuild').text = 'false' XML.SubElement(xml, "concurrentBuild").text = "false"
if 'workspace' in data: if "workspace" in data:
XML.SubElement(xml, 'customWorkspace').text = \ XML.SubElement(xml, "customWorkspace").text = str(data["workspace"])
str(data['workspace']) if (xml.tag == "matrix-project") and ("child-workspace" in data):
if (xml.tag == 'matrix-project') and ('child-workspace' in data): XML.SubElement(xml, "childCustomWorkspace").text = str(
XML.SubElement(xml, 'childCustomWorkspace').text = \ data["child-workspace"]
str(data['child-workspace']) )
if 'quiet-period' in data: if "quiet-period" in data:
XML.SubElement(xml, 'quietPeriod').text = str(data['quiet-period']) XML.SubElement(xml, "quietPeriod").text = str(data["quiet-period"])
node = data.get('node', None) node = data.get("node", None)
if node: if node:
XML.SubElement(xml, 'assignedNode').text = node XML.SubElement(xml, "assignedNode").text = node
XML.SubElement(xml, 'canRoam').text = 'false' XML.SubElement(xml, "canRoam").text = "false"
else: else:
XML.SubElement(xml, 'canRoam').text = 'true' XML.SubElement(xml, "canRoam").text = "true"
if 'retry-count' in data: if "retry-count" in data:
XML.SubElement(xml, 'scmCheckoutRetryCount').text = \ XML.SubElement(xml, "scmCheckoutRetryCount").text = str(data["retry-count"])
str(data['retry-count'])
if 'logrotate' in data: if "logrotate" in data:
if not self.logrotate_warn_issued: if not self.logrotate_warn_issued:
logging.warning('logrotate is deprecated on jenkins>=1.637,' logging.warning(
' the property build-discarder on newer' "logrotate is deprecated on jenkins>=1.637,"
' jenkins instead') " the property build-discarder on newer"
" jenkins instead"
)
self.logrotate_warn_issued = True self.logrotate_warn_issued = True
lr_xml = XML.SubElement(xml, 'logRotator') lr_xml = XML.SubElement(xml, "logRotator")
logrotate = data['logrotate'] logrotate = data["logrotate"]
lr_days = XML.SubElement(lr_xml, 'daysToKeep') lr_days = XML.SubElement(lr_xml, "daysToKeep")
lr_days.text = str(logrotate.get('daysToKeep', -1)) lr_days.text = str(logrotate.get("daysToKeep", -1))
lr_num = XML.SubElement(lr_xml, 'numToKeep') lr_num = XML.SubElement(lr_xml, "numToKeep")
lr_num.text = str(logrotate.get('numToKeep', -1)) lr_num.text = str(logrotate.get("numToKeep", -1))
lr_adays = XML.SubElement(lr_xml, 'artifactDaysToKeep') lr_adays = XML.SubElement(lr_xml, "artifactDaysToKeep")
lr_adays.text = str(logrotate.get('artifactDaysToKeep', -1)) lr_adays.text = str(logrotate.get("artifactDaysToKeep", -1))
lr_anum = XML.SubElement(lr_xml, 'artifactNumToKeep') lr_anum = XML.SubElement(lr_xml, "artifactNumToKeep")
lr_anum.text = str(logrotate.get('artifactNumToKeep', -1)) lr_anum.text = str(logrotate.get("artifactNumToKeep", -1))
if 'raw' in data: if "raw" in data:
raw(self.registry, xml, data['raw']) raw(self.registry, xml, data["raw"])
def raw(registry, xml_parent, data): def raw(registry, xml_parent, data):
# documented in definition.rst since includes and docs is not working well # documented in definition.rst since includes and docs is not working well
# For cross cutting method like this # For cross cutting method like this
root = XML.fromstring(data.get('xml')) root = XML.fromstring(data.get("xml"))
remove_ignorable_whitespace(root) remove_ignorable_whitespace(root)
xml_parent.append(root) xml_parent.append(root)

View File

@ -29,59 +29,62 @@ def build_trends_publisher(plugin_name, xml_element, data):
"""Appends the status thresholds. """Appends the status thresholds.
""" """
for status in ['unstable', 'failed']: for status in ["unstable", "failed"]:
status_data = data.get(status, {}) status_data = data.get(status, {})
limits = [ limits = [
('total-all', 'TotalAll'), ("total-all", "TotalAll"),
('total-high', 'TotalHigh'), ("total-high", "TotalHigh"),
('total-normal', 'TotalNormal'), ("total-normal", "TotalNormal"),
('total-low', 'TotalLow')] ("total-low", "TotalLow"),
]
if only_totals is False: if only_totals is False:
limits.extend([ limits.extend(
('new-all', 'NewAll'), [
('new-high', 'NewHigh'), ("new-all", "NewAll"),
('new-normal', 'NewNormal'), ("new-high", "NewHigh"),
('new-low', 'NewLow')]) ("new-normal", "NewNormal"),
("new-low", "NewLow"),
]
)
for key, tag_suffix in limits: for key, tag_suffix in limits:
tag_name = status + tag_suffix tag_name = status + tag_suffix
XML.SubElement(element, tag_name).text = str( XML.SubElement(element, tag_name).text = str(status_data.get(key, ""))
status_data.get(key, ''))
# Tuples containing: setting name, tag name, default value # Tuples containing: setting name, tag name, default value
settings = [ settings = [
('healthy', 'healthy', ''), ("healthy", "healthy", ""),
('unhealthy', 'unHealthy', ''), ("unhealthy", "unHealthy", ""),
('health-threshold', 'thresholdLimit', 'low'), ("health-threshold", "thresholdLimit", "low"),
('plugin-name', 'pluginName', plugin_name), ("plugin-name", "pluginName", plugin_name),
('default-encoding', 'defaultEncoding', ''), ("default-encoding", "defaultEncoding", ""),
('can-run-on-failed', 'canRunOnFailed', False), ("can-run-on-failed", "canRunOnFailed", False),
('use-stable-build-as-reference', 'useStableBuildAsReference', False), ("use-stable-build-as-reference", "useStableBuildAsReference", False),
('use-previous-build-as-reference', ("use-previous-build-as-reference", "usePreviousBuildAsReference", False),
'usePreviousBuildAsReference', False), ("use-delta-values", "useDeltaValues", False),
('use-delta-values', 'useDeltaValues', False), ("thresholds", "thresholds", {}),
('thresholds', 'thresholds', {}), ("should-detect-modules", "shouldDetectModules", False),
('should-detect-modules', 'shouldDetectModules', False), ("dont-compute-new", "dontComputeNew", True),
('dont-compute-new', 'dontComputeNew', True), ("do-not-resolve-relative-paths", "doNotResolveRelativePaths", False),
('do-not-resolve-relative-paths', 'doNotResolveRelativePaths', False), ("pattern", "pattern", ""),
('pattern', 'pattern', '')] ]
thresholds = ['low', 'normal', 'high'] thresholds = ["low", "normal", "high"]
for key, tag_name, default in settings: for key, tag_name, default in settings:
xml_config = XML.SubElement(xml_element, tag_name) xml_config = XML.SubElement(xml_element, tag_name)
config_value = data.get(key, default) config_value = data.get(key, default)
if key == 'thresholds': if key == "thresholds":
append_thresholds( append_thresholds(
xml_config, xml_config, config_value, data.get("dont-compute-new", True)
config_value, )
data.get('dont-compute-new', True)) elif key == "health-threshold" and config_value not in thresholds:
elif key == 'health-threshold' and config_value not in thresholds: raise JenkinsJobsException(
raise JenkinsJobsException("health-threshold must be one of %s" % "health-threshold must be one of %s" % ", ".join(thresholds)
", ".join(thresholds)) )
else: else:
if isinstance(default, bool): if isinstance(default, bool):
xml_config.text = str(config_value).lower() xml_config.text = str(config_value).lower()
@ -91,379 +94,385 @@ def build_trends_publisher(plugin_name, xml_element, data):
def config_file_provider_builder(xml_parent, data): def config_file_provider_builder(xml_parent, data):
"""Builder / Wrapper helper""" """Builder / Wrapper helper"""
xml_files = XML.SubElement(xml_parent, 'managedFiles') xml_files = XML.SubElement(xml_parent, "managedFiles")
files = data.get('files', []) files = data.get("files", [])
for file in files: for file in files:
xml_file = XML.SubElement(xml_files, 'org.jenkinsci.plugins.' xml_file = XML.SubElement(
'configfiles.buildwrapper.ManagedFile') xml_files, "org.jenkinsci.plugins." "configfiles.buildwrapper.ManagedFile"
)
mapping = [ mapping = [
('file-id', 'fileId', None), ("file-id", "fileId", None),
('target', 'targetLocation', ''), ("target", "targetLocation", ""),
('variable', 'variable', ''), ("variable", "variable", ""),
('replace-tokens', 'replaceTokens', False), ("replace-tokens", "replaceTokens", False),
] ]
convert_mapping_to_xml(xml_file, file, mapping, fail_required=True) convert_mapping_to_xml(xml_file, file, mapping, fail_required=True)
def config_file_provider_settings(xml_parent, data): def config_file_provider_settings(xml_parent, data):
SETTINGS_TYPES = ['file', 'cfp'] SETTINGS_TYPES = ["file", "cfp"]
settings = { settings = {
'default-settings': "default-settings": "jenkins.mvn.DefaultSettingsProvider",
'jenkins.mvn.DefaultSettingsProvider', "settings": "jenkins.mvn.FilePathSettingsProvider",
'settings': "config-file-provider-settings": "org.jenkinsci.plugins.configfiles.maven.job.MvnSettingsProvider",
'jenkins.mvn.FilePathSettingsProvider', "default-global-settings": "jenkins.mvn.DefaultGlobalSettingsProvider",
'config-file-provider-settings': "global-settings": "jenkins.mvn.FilePathGlobalSettingsProvider",
'org.jenkinsci.plugins.configfiles.maven.job.MvnSettingsProvider', "config-file-provider-global-settings": "org.jenkinsci.plugins.configfiles.maven.job."
'default-global-settings': "MvnGlobalSettingsProvider",
'jenkins.mvn.DefaultGlobalSettingsProvider',
'global-settings':
'jenkins.mvn.FilePathGlobalSettingsProvider',
'config-file-provider-global-settings':
'org.jenkinsci.plugins.configfiles.maven.job.'
'MvnGlobalSettingsProvider',
} }
if 'settings' in data: if "settings" in data:
# Support for Config File Provider # Support for Config File Provider
settings_file = str(data['settings']) settings_file = str(data["settings"])
settings_type = data.get('settings-type', 'file') settings_type = data.get("settings-type", "file")
# For cfp versions <2.10.0 we are able to detect cfp via the config # For cfp versions <2.10.0 we are able to detect cfp via the config
# settings name. # settings name.
text = 'org.jenkinsci.plugins.configfiles.maven.MavenSettingsConfig' text = "org.jenkinsci.plugins.configfiles.maven.MavenSettingsConfig"
if settings_file.startswith(text): if settings_file.startswith(text):
settings_type = 'cfp' settings_type = "cfp"
if settings_type == 'file': if settings_type == "file":
lsettings = XML.SubElement( lsettings = XML.SubElement(
xml_parent, 'settings', xml_parent, "settings", {"class": settings["settings"]}
{'class': settings['settings']}) )
XML.SubElement(lsettings, 'path').text = settings_file XML.SubElement(lsettings, "path").text = settings_file
elif settings_type == 'cfp': elif settings_type == "cfp":
lsettings = XML.SubElement( lsettings = XML.SubElement(
xml_parent, 'settings', xml_parent,
{'class': settings['config-file-provider-settings']}) "settings",
XML.SubElement(lsettings, 'settingsConfigId').text = settings_file {"class": settings["config-file-provider-settings"]},
)
XML.SubElement(lsettings, "settingsConfigId").text = settings_file
else: else:
raise InvalidAttributeError( raise InvalidAttributeError("settings-type", settings_type, SETTINGS_TYPES)
'settings-type', settings_type, SETTINGS_TYPES)
else: else:
XML.SubElement(xml_parent, 'settings', XML.SubElement(xml_parent, "settings", {"class": settings["default-settings"]})
{'class': settings['default-settings']})
if 'global-settings' in data: if "global-settings" in data:
# Support for Config File Provider # Support for Config File Provider
global_settings_file = str(data['global-settings']) global_settings_file = str(data["global-settings"])
global_settings_type = data.get('global-settings-type', 'file') global_settings_type = data.get("global-settings-type", "file")
# For cfp versions <2.10.0 we are able to detect cfp via the config # For cfp versions <2.10.0 we are able to detect cfp via the config
# settings name. # settings name.
text = ('org.jenkinsci.plugins.configfiles.maven.' text = "org.jenkinsci.plugins.configfiles.maven." "GlobalMavenSettingsConfig"
'GlobalMavenSettingsConfig')
if global_settings_file.startswith(text): if global_settings_file.startswith(text):
global_settings_type = 'cfp' global_settings_type = "cfp"
if global_settings_type == 'file': if global_settings_type == "file":
gsettings = XML.SubElement(xml_parent, 'globalSettings',
{'class': settings['global-settings']})
XML.SubElement(gsettings, 'path').text = global_settings_file
elif global_settings_type == 'cfp':
gsettings = XML.SubElement( gsettings = XML.SubElement(
xml_parent, 'globalSettings', xml_parent, "globalSettings", {"class": settings["global-settings"]}
{'class': settings['config-file-provider-global-settings']}) )
XML.SubElement( XML.SubElement(gsettings, "path").text = global_settings_file
gsettings, elif global_settings_type == "cfp":
'settingsConfigId').text = global_settings_file gsettings = XML.SubElement(
xml_parent,
"globalSettings",
{"class": settings["config-file-provider-global-settings"]},
)
XML.SubElement(gsettings, "settingsConfigId").text = global_settings_file
else: else:
raise InvalidAttributeError( raise InvalidAttributeError(
'settings-type', global_settings_type, SETTINGS_TYPES) "settings-type", global_settings_type, SETTINGS_TYPES
)
else: else:
XML.SubElement(xml_parent, 'globalSettings', XML.SubElement(
{'class': settings['default-global-settings']}) xml_parent, "globalSettings", {"class": settings["default-global-settings"]}
)
def copyartifact_build_selector(xml_parent, data, select_tag='selector'): def copyartifact_build_selector(xml_parent, data, select_tag="selector"):
select = data.get('which-build', 'last-successful') select = data.get("which-build", "last-successful")
selectdict = { selectdict = {
'last-successful': 'StatusBuildSelector', "last-successful": "StatusBuildSelector",
'last-completed': 'LastCompletedBuildSelector', "last-completed": "LastCompletedBuildSelector",
'specific-build': 'SpecificBuildSelector', "specific-build": "SpecificBuildSelector",
'last-saved': 'SavedBuildSelector', "last-saved": "SavedBuildSelector",
'upstream-build': 'TriggeredBuildSelector', "upstream-build": "TriggeredBuildSelector",
'permalink': 'PermalinkBuildSelector', "permalink": "PermalinkBuildSelector",
'workspace-latest': 'WorkspaceSelector', "workspace-latest": "WorkspaceSelector",
'build-param': 'ParameterizedBuildSelector', "build-param": "ParameterizedBuildSelector",
'downstream-build': 'DownstreamBuildSelector', "downstream-build": "DownstreamBuildSelector",
'multijob-build': 'MultiJobBuildSelector' "multijob-build": "MultiJobBuildSelector",
} }
if select not in selectdict: if select not in selectdict:
raise InvalidAttributeError('which-build', raise InvalidAttributeError("which-build", select, selectdict.keys())
select, permalink = data.get("permalink", "last")
selectdict.keys()) permalinkdict = {
permalink = data.get('permalink', 'last') "last": "lastBuild",
permalinkdict = {'last': 'lastBuild', "last-stable": "lastStableBuild",
'last-stable': 'lastStableBuild', "last-successful": "lastSuccessfulBuild",
'last-successful': 'lastSuccessfulBuild', "last-failed": "lastFailedBuild",
'last-failed': 'lastFailedBuild', "last-unstable": "lastUnstableBuild",
'last-unstable': 'lastUnstableBuild', "last-unsuccessful": "lastUnsuccessfulBuild",
'last-unsuccessful': 'lastUnsuccessfulBuild'} }
if permalink not in permalinkdict: if permalink not in permalinkdict:
raise InvalidAttributeError('permalink', raise InvalidAttributeError("permalink", permalink, permalinkdict.keys())
permalink, if select == "multijob-build":
permalinkdict.keys()) selector = XML.SubElement(
if select == 'multijob-build': xml_parent,
selector = XML.SubElement(xml_parent, select_tag, select_tag,
{'class': {"class": "com.tikal.jenkins.plugins.multijob." + selectdict[select]},
'com.tikal.jenkins.plugins.multijob.' + )
selectdict[select]})
else: else:
selector = XML.SubElement(xml_parent, select_tag, selector = XML.SubElement(
{'class': xml_parent,
'hudson.plugins.copyartifact.' + select_tag,
selectdict[select]}) {"class": "hudson.plugins.copyartifact." + selectdict[select]},
)
mapping = [] mapping = []
if select == 'specific-build': if select == "specific-build":
mapping.append(('build-number', 'buildNumber', '')) mapping.append(("build-number", "buildNumber", ""))
if select == 'last-successful': if select == "last-successful":
mapping.append(('stable', 'stable', False)) mapping.append(("stable", "stable", False))
if select == 'upstream-build': if select == "upstream-build":
mapping.append( mapping.append(
('fallback-to-last-successful', 'fallbackToLastSuccessful', False)) ("fallback-to-last-successful", "fallbackToLastSuccessful", False)
if select == 'permalink': )
mapping.append(('', 'id', permalinkdict[permalink])) if select == "permalink":
if select == 'build-param': mapping.append(("", "id", permalinkdict[permalink]))
mapping.append(('param', 'parameterName', '')) if select == "build-param":
if select == 'downstream-build': mapping.append(("param", "parameterName", ""))
mapping.append( if select == "downstream-build":
('upstream-project-name', 'upstreamProjectName', '')) mapping.append(("upstream-project-name", "upstreamProjectName", ""))
mapping.append( mapping.append(("upstream-build-number", "upstreamBuildNumber", ""))
('upstream-build-number', 'upstreamBuildNumber', ''))
convert_mapping_to_xml(selector, data, mapping, fail_required=False) convert_mapping_to_xml(selector, data, mapping, fail_required=False)
def findbugs_settings(xml_parent, data): def findbugs_settings(xml_parent, data):
# General Options # General Options
mapping = [ mapping = [
('rank-priority', 'isRankActivated', False), ("rank-priority", "isRankActivated", False),
('include-files', 'includePattern', ''), ("include-files", "includePattern", ""),
('exclude-files', 'excludePattern', ''), ("exclude-files", "excludePattern", ""),
] ]
convert_mapping_to_xml(xml_parent, data, mapping, fail_required=True) convert_mapping_to_xml(xml_parent, data, mapping, fail_required=True)
def get_value_from_yaml_or_config_file(key, section, data, jjb_config): def get_value_from_yaml_or_config_file(key, section, data, jjb_config):
return jjb_config.get_plugin_config(section, key, data.get(key, '')) return jjb_config.get_plugin_config(section, key, data.get(key, ""))
def cloudformation_region_dict(): def cloudformation_region_dict():
region_dict = {'us-east-1': 'US_East_Northern_Virginia', region_dict = {
'us-west-1': 'US_WEST_Northern_California', "us-east-1": "US_East_Northern_Virginia",
'us-west-2': 'US_WEST_Oregon', "us-west-1": "US_WEST_Northern_California",
'eu-central-1': 'EU_Frankfurt', "us-west-2": "US_WEST_Oregon",
'eu-west-1': 'EU_Ireland', "eu-central-1": "EU_Frankfurt",
'ap-southeast-1': 'Asia_Pacific_Singapore', "eu-west-1": "EU_Ireland",
'ap-southeast-2': 'Asia_Pacific_Sydney', "ap-southeast-1": "Asia_Pacific_Singapore",
'ap-northeast-1': 'Asia_Pacific_Tokyo', "ap-southeast-2": "Asia_Pacific_Sydney",
'sa-east-1': 'South_America_Sao_Paulo'} "ap-northeast-1": "Asia_Pacific_Tokyo",
"sa-east-1": "South_America_Sao_Paulo",
}
return region_dict return region_dict
def cloudformation_init(xml_parent, data, xml_tag): def cloudformation_init(xml_parent, data, xml_tag):
cloudformation = XML.SubElement( cloudformation = XML.SubElement(
xml_parent, 'com.syncapse.jenkinsci.' xml_parent,
'plugins.awscloudformationwrapper.' + xml_tag) "com.syncapse.jenkinsci." "plugins.awscloudformationwrapper." + xml_tag,
return XML.SubElement(cloudformation, 'stacks') )
return XML.SubElement(cloudformation, "stacks")
def cloudformation_stack(xml_parent, stack, xml_tag, stacks, region_dict): def cloudformation_stack(xml_parent, stack, xml_tag, stacks, region_dict):
if 'name' not in stack or stack['name'] == '': if "name" not in stack or stack["name"] == "":
raise MissingAttributeError('name') raise MissingAttributeError("name")
step = XML.SubElement( step = XML.SubElement(
stacks, 'com.syncapse.jenkinsci.plugins.' stacks, "com.syncapse.jenkinsci.plugins." "awscloudformationwrapper." + xml_tag
'awscloudformationwrapper.' + xml_tag) )
if xml_tag == 'SimpleStackBean': if xml_tag == "SimpleStackBean":
mapping = [('prefix', 'isPrefixSelected', False)] mapping = [("prefix", "isPrefixSelected", False)]
else: else:
parameters_value = ','.join(stack.get('parameters', [])) parameters_value = ",".join(stack.get("parameters", []))
mapping = [ mapping = [
('description', 'description', ''), ("description", "description", ""),
('', 'parameters', parameters_value), ("", "parameters", parameters_value),
('timeout', 'timeout', '0'), ("timeout", "timeout", "0"),
('sleep', 'sleep', '0'), ("sleep", "sleep", "0"),
('recipe', 'cloudFormationRecipe', None)] ("recipe", "cloudFormationRecipe", None),
]
cloudformation_stack_mapping = [ cloudformation_stack_mapping = [
('name', 'stackName', None), ("name", "stackName", None),
('access-key', 'awsAccessKey', None), ("access-key", "awsAccessKey", None),
('secret-key', 'awsSecretKey', None), ("secret-key", "awsSecretKey", None),
('region', 'awsRegion', None, region_dict)] ("region", "awsRegion", None, region_dict),
]
for map in mapping: for map in mapping:
cloudformation_stack_mapping.append(map) cloudformation_stack_mapping.append(map)
convert_mapping_to_xml(step, stack, convert_mapping_to_xml(
cloudformation_stack_mapping, fail_required=True) step, stack, cloudformation_stack_mapping, fail_required=True
)
def include_exclude_patterns(xml_parent, data, yaml_prefix, def include_exclude_patterns(xml_parent, data, yaml_prefix, xml_elem_name):
xml_elem_name):
xml_element = XML.SubElement(xml_parent, xml_elem_name) xml_element = XML.SubElement(xml_parent, xml_elem_name)
XML.SubElement(xml_element, 'includePatterns').text = ','.join( XML.SubElement(xml_element, "includePatterns").text = ",".join(
data.get(yaml_prefix + '-include-patterns', [])) data.get(yaml_prefix + "-include-patterns", [])
XML.SubElement(xml_element, 'excludePatterns').text = ','.join( )
data.get(yaml_prefix + '-exclude-patterns', [])) XML.SubElement(xml_element, "excludePatterns").text = ",".join(
data.get(yaml_prefix + "-exclude-patterns", [])
)
def artifactory_deployment_patterns(xml_parent, data): def artifactory_deployment_patterns(xml_parent, data):
include_exclude_patterns(xml_parent, data, 'deployment', include_exclude_patterns(
'artifactDeploymentPatterns') xml_parent, data, "deployment", "artifactDeploymentPatterns"
)
def artifactory_env_vars_patterns(xml_parent, data): def artifactory_env_vars_patterns(xml_parent, data):
include_exclude_patterns(xml_parent, data, 'env-vars', include_exclude_patterns(xml_parent, data, "env-vars", "envVarsPatterns")
'envVarsPatterns')
def artifactory_optional_props(xml_parent, data, target): def artifactory_optional_props(xml_parent, data, target):
optional_str_props = [ optional_str_props = [
('scopes', 'scopes'), ("scopes", "scopes"),
('violationRecipients', 'violation-recipients'), ("violationRecipients", "violation-recipients"),
('blackDuckAppName', 'black-duck-app-name'), ("blackDuckAppName", "black-duck-app-name"),
('blackDuckAppVersion', 'black-duck-app-version'), ("blackDuckAppVersion", "black-duck-app-version"),
('blackDuckReportRecipients', 'black-duck-report-recipients'), ("blackDuckReportRecipients", "black-duck-report-recipients"),
('blackDuckScopes', 'black-duck-scopes') ("blackDuckScopes", "black-duck-scopes"),
] ]
for (xml_prop, yaml_prop) in optional_str_props: for (xml_prop, yaml_prop) in optional_str_props:
XML.SubElement(xml_parent, xml_prop).text = data.get( XML.SubElement(xml_parent, xml_prop).text = data.get(yaml_prop, "")
yaml_prop, '')
common_bool_props = [ common_bool_props = [
# yaml property name, xml property name, default value # yaml property name, xml property name, default value
('deploy-artifacts', 'deployArtifacts', True), ("deploy-artifacts", "deployArtifacts", True),
('discard-old-builds', 'discardOldBuilds', False), ("discard-old-builds", "discardOldBuilds", False),
('discard-build-artifacts', 'discardBuildArtifacts', False), ("discard-build-artifacts", "discardBuildArtifacts", False),
('publish-build-info', 'deployBuildInfo', False), ("publish-build-info", "deployBuildInfo", False),
('env-vars-include', 'includeEnvVars', False), ("env-vars-include", "includeEnvVars", False),
('run-checks', 'runChecks', False), ("run-checks", "runChecks", False),
('include-publish-artifacts', 'includePublishArtifacts', False), ("include-publish-artifacts", "includePublishArtifacts", False),
('license-auto-discovery', 'licenseAutoDiscovery', True), ("license-auto-discovery", "licenseAutoDiscovery", True),
('enable-issue-tracker-integration', 'enableIssueTrackerIntegration', ("enable-issue-tracker-integration", "enableIssueTrackerIntegration", False),
False), ("aggregate-build-issues", "aggregateBuildIssues", False),
('aggregate-build-issues', 'aggregateBuildIssues', False), ("black-duck-run-checks", "blackDuckRunChecks", False),
('black-duck-run-checks', 'blackDuckRunChecks', False), (
('black-duck-include-published-artifacts', "black-duck-include-published-artifacts",
'blackDuckIncludePublishedArtifacts', False), "blackDuckIncludePublishedArtifacts",
('auto-create-missing-component-requests', False,
'autoCreateMissingComponentRequests', True), ),
('auto-discard-stale-component-requests', (
'autoDiscardStaleComponentRequests', True), "auto-create-missing-component-requests",
('filter-excluded-artifacts-from-build', "autoCreateMissingComponentRequests",
'filterExcludedArtifactsFromBuild', False) True,
),
(
"auto-discard-stale-component-requests",
"autoDiscardStaleComponentRequests",
True,
),
(
"filter-excluded-artifacts-from-build",
"filterExcludedArtifactsFromBuild",
False,
),
] ]
convert_mapping_to_xml( convert_mapping_to_xml(xml_parent, data, common_bool_props, fail_required=True)
xml_parent, data, common_bool_props, fail_required=True)
if 'wrappers' in target: if "wrappers" in target:
wrapper_bool_props = [ wrapper_bool_props = [
('enable-resolve-artifacts', 'enableResolveArtifacts', False), ("enable-resolve-artifacts", "enableResolveArtifacts", False),
('disable-license-auto-discovery', ("disable-license-auto-discovery", "disableLicenseAutoDiscovery", False),
'disableLicenseAutoDiscovery', False), ("record-all-dependencies", "recordAllDependencies", False),
('record-all-dependencies',
'recordAllDependencies', False)
] ]
convert_mapping_to_xml( convert_mapping_to_xml(xml_parent, data, wrapper_bool_props, fail_required=True)
xml_parent, data, wrapper_bool_props, fail_required=True)
if 'publishers' in target: if "publishers" in target:
publisher_bool_props = [ publisher_bool_props = [
('even-if-unstable', 'evenIfUnstable', False), ("even-if-unstable", "evenIfUnstable", False),
('pass-identified-downstream', 'passIdentifiedDownstream', False), ("pass-identified-downstream", "passIdentifiedDownstream", False),
('allow-promotion-of-non-staged-builds', (
'allowPromotionOfNonStagedBuilds', False) "allow-promotion-of-non-staged-builds",
"allowPromotionOfNonStagedBuilds",
False,
),
] ]
convert_mapping_to_xml( convert_mapping_to_xml(
xml_parent, data, publisher_bool_props, fail_required=True) xml_parent, data, publisher_bool_props, fail_required=True
)
def artifactory_common_details(details, data): def artifactory_common_details(details, data):
mapping = [ mapping = [("name", "artifactoryName", ""), ("url", "artifactoryUrl", "")]
('name', 'artifactoryName', ''),
('url', 'artifactoryUrl', ''),
]
convert_mapping_to_xml(details, data, mapping, fail_required=True) convert_mapping_to_xml(details, data, mapping, fail_required=True)
def artifactory_repository(xml_parent, data, target): def artifactory_repository(xml_parent, data, target):
if 'release' in target: if "release" in target:
release_mapping = [ release_mapping = [
('deploy-release-repo-key', 'keyFromText', ''), ("deploy-release-repo-key", "keyFromText", ""),
('deploy-release-repo-key', 'keyFromSelect', ''), ("deploy-release-repo-key", "keyFromSelect", ""),
('deploy-dynamic-mode', 'dynamicMode', False), ("deploy-dynamic-mode", "dynamicMode", False),
] ]
convert_mapping_to_xml( convert_mapping_to_xml(xml_parent, data, release_mapping, fail_required=True)
xml_parent, data, release_mapping, fail_required=True)
if 'snapshot' in target: if "snapshot" in target:
snapshot_mapping = [ snapshot_mapping = [
('deploy-snapshot-repo-key', 'keyFromText', ''), ("deploy-snapshot-repo-key", "keyFromText", ""),
('deploy-snapshot-repo-key', 'keyFromSelect', ''), ("deploy-snapshot-repo-key", "keyFromSelect", ""),
('deploy-dynamic-mode', 'dynamicMode', False), ("deploy-dynamic-mode", "dynamicMode", False),
] ]
convert_mapping_to_xml( convert_mapping_to_xml(xml_parent, data, snapshot_mapping, fail_required=True)
xml_parent, data, snapshot_mapping, fail_required=True)
def append_git_revision_config(parent, config_def): def append_git_revision_config(parent, config_def):
params = XML.SubElement( params = XML.SubElement(parent, "hudson.plugins.git.GitRevisionBuildParameters")
parent, 'hudson.plugins.git.GitRevisionBuildParameters')
try: try:
# If git-revision is a boolean, the get() will # If git-revision is a boolean, the get() will
# throw an AttributeError # throw an AttributeError
combine_commits = str( combine_commits = str(config_def.get("combine-queued-commits", False)).lower()
config_def.get('combine-queued-commits', False)).lower()
except AttributeError: except AttributeError:
combine_commits = 'false' combine_commits = "false"
XML.SubElement(params, 'combineQueuedCommits').text = combine_commits XML.SubElement(params, "combineQueuedCommits").text = combine_commits
def test_fairy_common(xml_element, data): def test_fairy_common(xml_element, data):
xml_element.set('plugin', 'TestFairy') xml_element.set("plugin", "TestFairy")
valid_max_duration = ['10m', '60m', '300m', '1440m'] valid_max_duration = ["10m", "60m", "300m", "1440m"]
valid_interval = [1, 2, 5] valid_interval = [1, 2, 5]
valid_video_quality = ['high', 'medium', 'low'] valid_video_quality = ["high", "medium", "low"]
mappings = [ mappings = [
# General # General
('apikey', 'apiKey', None), ("apikey", "apiKey", None),
('appfile', 'appFile', None), ("appfile", "appFile", None),
('tester-groups', 'testersGroups', ''), ("tester-groups", "testersGroups", ""),
('notify-testers', 'notifyTesters', True), ("notify-testers", "notifyTesters", True),
('autoupdate', 'autoUpdate', True), ("autoupdate", "autoUpdate", True),
# Session # Session
('max-duration', 'maxDuration', '10m', valid_max_duration), ("max-duration", "maxDuration", "10m", valid_max_duration),
('record-on-background', 'recordOnBackground', False), ("record-on-background", "recordOnBackground", False),
('data-only-wifi', 'dataOnlyWifi', False), ("data-only-wifi", "dataOnlyWifi", False),
# Video # Video
('video-enabled', 'isVideoEnabled', True), ("video-enabled", "isVideoEnabled", True),
('screenshot-interval', 'screenshotInterval', 1, valid_interval), ("screenshot-interval", "screenshotInterval", 1, valid_interval),
('video-quality', 'videoQuality', 'high', valid_video_quality), ("video-quality", "videoQuality", "high", valid_video_quality),
# Metrics # Metrics
('cpu', 'cpu', True), ("cpu", "cpu", True),
('memory', 'memory', True), ("memory", "memory", True),
('logs', 'logs', True), ("logs", "logs", True),
('network', 'network', False), ("network", "network", False),
('phone-signal', 'phoneSignal', False), ("phone-signal", "phoneSignal", False),
('wifi', 'wifi', False), ("wifi", "wifi", False),
('gps', 'gps', False), ("gps", "gps", False),
('battery', 'battery', False), ("battery", "battery", False),
('opengl', 'openGl', False), ("opengl", "openGl", False),
# Advanced options # Advanced options
('advanced-options', 'advancedOptions', '') ("advanced-options", "advancedOptions", ""),
] ]
convert_mapping_to_xml(xml_element, data, mappings, fail_required=True) convert_mapping_to_xml(xml_element, data, mappings, fail_required=True)
@ -471,25 +480,31 @@ def test_fairy_common(xml_element, data):
def trigger_get_parameter_order(registry, plugin): def trigger_get_parameter_order(registry, plugin):
logger = logging.getLogger("%s:trigger_get_parameter_order" % __name__) logger = logging.getLogger("%s:trigger_get_parameter_order" % __name__)
if str(registry.jjb_config.get_plugin_config( if (
plugin, 'param_order_from_yaml', True)).lower() == 'false': str(
registry.jjb_config.get_plugin_config(plugin, "param_order_from_yaml", True)
).lower()
== "false"
):
logger.warning( logger.warning(
"Using deprecated order for parameter sets in %s. It is " "Using deprecated order for parameter sets in %s. It is "
"recommended that you update your job definition instead of " "recommended that you update your job definition instead of "
"enabling use of the old hardcoded order", plugin) "enabling use of the old hardcoded order",
plugin,
)
# deprecated order # deprecated order
return [ return [
'predefined-parameters', "predefined-parameters",
'git-revision', "git-revision",
'property-file', "property-file",
'current-parameters', "current-parameters",
'node-parameters', "node-parameters",
'svn-revision', "svn-revision",
'restrict-matrix-project', "restrict-matrix-project",
'node-label-name', "node-label-name",
'node-label', "node-label",
'boolean-parameters', "boolean-parameters",
] ]
return None return None
@ -498,7 +513,7 @@ def trigger_get_parameter_order(registry, plugin):
def trigger_project(tconfigs, project_def, param_order=None): def trigger_project(tconfigs, project_def, param_order=None):
logger = logging.getLogger("%s:trigger_project" % __name__) logger = logging.getLogger("%s:trigger_project" % __name__)
pt_prefix = 'hudson.plugins.parameterizedtrigger.' pt_prefix = "hudson.plugins.parameterizedtrigger."
if param_order: if param_order:
parameters = param_order parameters = param_order
else: else:
@ -509,88 +524,93 @@ def trigger_project(tconfigs, project_def, param_order=None):
if param_value is None: if param_value is None:
continue continue
if param_type == 'predefined-parameters': if param_type == "predefined-parameters":
params = XML.SubElement(tconfigs, pt_prefix + params = XML.SubElement(tconfigs, pt_prefix + "PredefinedBuildParameters")
'PredefinedBuildParameters') properties = XML.SubElement(params, "properties")
properties = XML.SubElement(params, 'properties')
properties.text = param_value properties.text = param_value
elif param_type == 'git-revision' and param_value: elif param_type == "git-revision" and param_value:
if 'combine-queued-commits' in project_def: if "combine-queued-commits" in project_def:
logger.warning( logger.warning(
"'combine-queued-commit' has moved to reside under " "'combine-queued-commit' has moved to reside under "
"'git-revision' configuration, please update your " "'git-revision' configuration, please update your "
"configs as support for this will be removed." "configs as support for this will be removed."
) )
git_revision = { git_revision = {
'combine-queued-commits': "combine-queued-commits": project_def["combine-queued-commits"]
project_def['combine-queued-commits']
} }
else: else:
git_revision = project_def['git-revision'] git_revision = project_def["git-revision"]
append_git_revision_config(tconfigs, git_revision) append_git_revision_config(tconfigs, git_revision)
elif param_type == 'property-file': elif param_type == "property-file":
params = XML.SubElement(tconfigs, params = XML.SubElement(tconfigs, pt_prefix + "FileBuildParameters")
pt_prefix + 'FileBuildParameters')
property_file_mapping = [ property_file_mapping = [
('property-file', 'propertiesFile', None), ("property-file", "propertiesFile", None),
('fail-on-missing', 'failTriggerOnMissing', False)] ("fail-on-missing", "failTriggerOnMissing", False),
convert_mapping_to_xml(params, project_def, ]
property_file_mapping, fail_required=True) convert_mapping_to_xml(
if 'file-encoding' in project_def: params, project_def, property_file_mapping, fail_required=True
XML.SubElement(params, 'encoding' )
).text = project_def['file-encoding'] if "file-encoding" in project_def:
if 'use-matrix-child-files' in project_def: XML.SubElement(params, "encoding").text = project_def["file-encoding"]
if "use-matrix-child-files" in project_def:
# TODO: These parameters only affect execution in # TODO: These parameters only affect execution in
# publishers of matrix projects; we should warn if they are # publishers of matrix projects; we should warn if they are
# used in other contexts. # used in other contexts.
use_matrix_child_files_mapping = [ use_matrix_child_files_mapping = [
('use-matrix-child-files', "useMatrixChild", None), ("use-matrix-child-files", "useMatrixChild", None),
('matrix-child-combination-filter', ("matrix-child-combination-filter", "combinationFilter", ""),
"combinationFilter", ''), ("only-exact-matrix-child-runs", "onlyExactRuns", False),
('only-exact-matrix-child-runs', "onlyExactRuns", False)] ]
convert_mapping_to_xml(params, project_def, convert_mapping_to_xml(
use_matrix_child_files_mapping, fail_required=True) params,
elif param_type == 'current-parameters' and param_value: project_def,
XML.SubElement(tconfigs, pt_prefix + 'CurrentBuildParameters') use_matrix_child_files_mapping,
elif param_type == 'node-parameters' and param_value: fail_required=True,
XML.SubElement(tconfigs, pt_prefix + 'NodeParameters') )
elif param_type == 'svn-revision' and param_value: elif param_type == "current-parameters" and param_value:
param = XML.SubElement(tconfigs, pt_prefix + XML.SubElement(tconfigs, pt_prefix + "CurrentBuildParameters")
'SubversionRevisionBuildParameters') elif param_type == "node-parameters" and param_value:
XML.SubElement(param, 'includeUpstreamParameters').text = str( XML.SubElement(tconfigs, pt_prefix + "NodeParameters")
project_def.get('include-upstream', False)).lower() elif param_type == "svn-revision" and param_value:
elif param_type == 'restrict-matrix-project' and param_value: param = XML.SubElement(
subset = XML.SubElement(tconfigs, pt_prefix + tconfigs, pt_prefix + "SubversionRevisionBuildParameters"
'matrix.MatrixSubsetBuildParameters') )
XML.SubElement(subset, 'filter' XML.SubElement(param, "includeUpstreamParameters").text = str(
).text = project_def['restrict-matrix-project'] project_def.get("include-upstream", False)
elif (param_type == 'node-label-name' or ).lower()
param_type == 'node-label'): elif param_type == "restrict-matrix-project" and param_value:
tag_name = ('org.jvnet.jenkins.plugins.nodelabelparameter.' subset = XML.SubElement(
'parameterizedtrigger.NodeLabelBuildParameter') tconfigs, pt_prefix + "matrix.MatrixSubsetBuildParameters"
)
XML.SubElement(subset, "filter").text = project_def[
"restrict-matrix-project"
]
elif param_type == "node-label-name" or param_type == "node-label":
tag_name = (
"org.jvnet.jenkins.plugins.nodelabelparameter."
"parameterizedtrigger.NodeLabelBuildParameter"
)
if tconfigs.find(tag_name) is not None: if tconfigs.find(tag_name) is not None:
# already processed and can only have one # already processed and can only have one
continue continue
params = XML.SubElement(tconfigs, tag_name) params = XML.SubElement(tconfigs, tag_name)
name = XML.SubElement(params, 'name') name = XML.SubElement(params, "name")
if 'node-label-name' in project_def: if "node-label-name" in project_def:
name.text = project_def['node-label-name'] name.text = project_def["node-label-name"]
label = XML.SubElement(params, 'nodeLabel') label = XML.SubElement(params, "nodeLabel")
if 'node-label' in project_def: if "node-label" in project_def:
label.text = project_def['node-label'] label.text = project_def["node-label"]
elif param_type == 'boolean-parameters' and param_value: elif param_type == "boolean-parameters" and param_value:
params = XML.SubElement(tconfigs, params = XML.SubElement(tconfigs, pt_prefix + "BooleanParameters")
pt_prefix + 'BooleanParameters') config_tag = XML.SubElement(params, "configs")
config_tag = XML.SubElement(params, 'configs') param_tag_text = pt_prefix + "BooleanParameterConfig"
param_tag_text = pt_prefix + 'BooleanParameterConfig'
params_list = param_value params_list = param_value
for name, value in params_list.items(): for name, value in params_list.items():
param_tag = XML.SubElement(config_tag, param_tag_text) param_tag = XML.SubElement(config_tag, param_tag_text)
mapping = [ mapping = [("", "name", name), ("", "value", value or False)]
('', 'name', name), convert_mapping_to_xml(
('', 'value', value or False)] param_tag, project_def, mapping, fail_required=True
convert_mapping_to_xml(param_tag, project_def, )
mapping, fail_required=True)
def convert_mapping_to_xml(parent, data, mapping, fail_required=True): def convert_mapping_to_xml(parent, data, mapping, fail_required=True):
@ -661,19 +681,17 @@ def jms_messaging_common(parent, subelement, data):
data is passed to mapper helper function to map yaml fields to XML fields data is passed to mapper helper function to map yaml fields to XML fields
""" """
namespace = XML.SubElement(parent, namespace = XML.SubElement(parent, subelement)
subelement)
if 'override-topic' in data: if "override-topic" in data:
overrides = XML.SubElement(namespace, 'overrides') overrides = XML.SubElement(namespace, "overrides")
XML.SubElement(overrides, XML.SubElement(overrides, "topic").text = str(data.get("override-topic", ""))
'topic').text = str(data.get('override-topic', ''))
mapping = [ mapping = [
# option, xml name, default value # option, xml name, default value
("provider-name", 'providerName', ''), ("provider-name", "providerName", ""),
("msg-type", 'messageType', 'CodeQualityChecksDone'), ("msg-type", "messageType", "CodeQualityChecksDone"),
("msg-props", 'messageProperties', ''), ("msg-props", "messageProperties", ""),
("msg-content", 'messageContent', ''), ("msg-content", "messageContent", ""),
] ]
convert_mapping_to_xml(namespace, data, mapping, fail_required=True) convert_mapping_to_xml(namespace, data, mapping, fail_required=True)

View File

@ -104,95 +104,99 @@ class HipChat(jenkins_jobs.modules.base.Base):
jjb_config = self.registry.jjb_config jjb_config = self.registry.jjb_config
if not self.authToken: if not self.authToken:
try: try:
self.authToken = jjb_config.get_plugin_config('hipchat', self.authToken = jjb_config.get_plugin_config("hipchat", "authtoken")
'authtoken')
# Require that the authtoken is non-null # Require that the authtoken is non-null
if self.authToken == '': if self.authToken == "":
raise jenkins_jobs.errors.JenkinsJobsException( raise jenkins_jobs.errors.JenkinsJobsException(
"Hipchat authtoken must not be a blank string") "Hipchat authtoken must not be a blank string"
except (configparser.NoSectionError, )
jenkins_jobs.errors.JenkinsJobsException) as e: except (
logger.fatal("The configuration file needs a hipchat section" + configparser.NoSectionError,
" containing authtoken:\n{0}".format(e)) jenkins_jobs.errors.JenkinsJobsException,
) as e:
logger.fatal(
"The configuration file needs a hipchat section"
+ " containing authtoken:\n{0}".format(e)
)
sys.exit(1) sys.exit(1)
self.jenkinsUrl = jjb_config.get_plugin_config('hipchat', 'url') self.jenkinsUrl = jjb_config.get_plugin_config("hipchat", "url")
self.sendAs = jjb_config.get_plugin_config('hipchat', 'send-as') self.sendAs = jjb_config.get_plugin_config("hipchat", "send-as")
def gen_xml(self, xml_parent, data): def gen_xml(self, xml_parent, data):
hipchat = data.get('hipchat') hipchat = data.get("hipchat")
if not hipchat or not hipchat.get('enabled', True): if not hipchat or not hipchat.get("enabled", True):
return return
self._load_global_data() self._load_global_data()
# convert for compatibility before dispatch # convert for compatibility before dispatch
if 'room' in hipchat: if "room" in hipchat:
if 'rooms' in hipchat: if "rooms" in hipchat:
logger.warning("Ignoring deprecated 'room' as 'rooms' also " logger.warning("Ignoring deprecated 'room' as 'rooms' also " "defined.")
"defined.")
else: else:
logger.warning("'room' is deprecated, please use 'rooms'") logger.warning("'room' is deprecated, please use 'rooms'")
hipchat['rooms'] = [hipchat['room']] hipchat["rooms"] = [hipchat["room"]]
plugin_info = self.registry.get_plugin_info("Jenkins HipChat Plugin") plugin_info = self.registry.get_plugin_info("Jenkins HipChat Plugin")
version = pkg_resources.parse_version(plugin_info.get('version', '0')) version = pkg_resources.parse_version(plugin_info.get("version", "0"))
if version >= pkg_resources.parse_version("0.1.9"): if version >= pkg_resources.parse_version("0.1.9"):
publishers = xml_parent.find('publishers') publishers = xml_parent.find("publishers")
if publishers is None: if publishers is None:
publishers = XML.SubElement(xml_parent, 'publishers') publishers = XML.SubElement(xml_parent, "publishers")
logger.warning( logger.warning(
"'hipchat' module supports the old plugin versions <1.9, " "'hipchat' module supports the old plugin versions <1.9, "
"newer versions are supported via the 'publishers' module. " "newer versions are supported via the 'publishers' module. "
"Please upgrade you job definition") "Please upgrade you job definition"
component = {'hipchat': hipchat} )
return self.registry.dispatch('publisher', publishers, component) component = {"hipchat": hipchat}
return self.registry.dispatch("publisher", publishers, component)
else: else:
properties = xml_parent.find('properties') properties = xml_parent.find("properties")
if properties is None: if properties is None:
properties = XML.SubElement(xml_parent, 'properties') properties = XML.SubElement(xml_parent, "properties")
pdefhip = XML.SubElement(properties, pdefhip = XML.SubElement(
'jenkins.plugins.hipchat.' properties,
'HipChatNotifier_-HipChatJobProperty') "jenkins.plugins.hipchat." "HipChatNotifier_-HipChatJobProperty",
)
room = XML.SubElement(pdefhip, 'room') room = XML.SubElement(pdefhip, "room")
if 'rooms' in hipchat: if "rooms" in hipchat:
room.text = ",".join(hipchat['rooms']) room.text = ",".join(hipchat["rooms"])
# Handle backwards compatibility 'start-notify' but all add an element # Handle backwards compatibility 'start-notify' but all add an element
# of standardization with notify-* # of standardization with notify-*
if hipchat.get('start-notify'): if hipchat.get("start-notify"):
logger.warning("'start-notify' is deprecated, please use " logger.warning("'start-notify' is deprecated, please use " "'notify-start'")
"'notify-start'") XML.SubElement(pdefhip, "startNotification").text = str(
XML.SubElement(pdefhip, 'startNotification').text = str( hipchat.get("notify-start", hipchat.get("start-notify", False))
hipchat.get('notify-start', hipchat.get('start-notify', ).lower()
False))).lower()
if version >= pkg_resources.parse_version("0.1.5"): if version >= pkg_resources.parse_version("0.1.5"):
mapping = [ mapping = [
('notify-success', 'notifySuccess', False), ("notify-success", "notifySuccess", False),
('notify-aborted', 'notifyAborted', False), ("notify-aborted", "notifyAborted", False),
('notify-not-built', 'notifyNotBuilt', False), ("notify-not-built", "notifyNotBuilt", False),
('notify-unstable', 'notifyUnstable', False), ("notify-unstable", "notifyUnstable", False),
('notify-failure', 'notifyFailure', False), ("notify-failure", "notifyFailure", False),
('notify-back-to-normal', 'notifyBackToNormal', False), ("notify-back-to-normal", "notifyBackToNormal", False),
] ]
helpers.convert_mapping_to_xml(pdefhip, helpers.convert_mapping_to_xml(
hipchat, mapping, fail_required=True) pdefhip, hipchat, mapping, fail_required=True
)
publishers = xml_parent.find('publishers') publishers = xml_parent.find("publishers")
if publishers is None: if publishers is None:
publishers = XML.SubElement(xml_parent, 'publishers') publishers = XML.SubElement(xml_parent, "publishers")
hippub = XML.SubElement(publishers, hippub = XML.SubElement(publishers, "jenkins.plugins.hipchat.HipChatNotifier")
'jenkins.plugins.hipchat.HipChatNotifier')
if version >= pkg_resources.parse_version("0.1.8"): if version >= pkg_resources.parse_version("0.1.8"):
XML.SubElement(hippub, 'buildServerUrl').text = self.jenkinsUrl XML.SubElement(hippub, "buildServerUrl").text = self.jenkinsUrl
XML.SubElement(hippub, 'sendAs').text = self.sendAs XML.SubElement(hippub, "sendAs").text = self.sendAs
else: else:
XML.SubElement(hippub, 'jenkinsUrl').text = self.jenkinsUrl XML.SubElement(hippub, "jenkinsUrl").text = self.jenkinsUrl
XML.SubElement(hippub, 'authToken').text = self.authToken XML.SubElement(hippub, "authToken").text = self.authToken
# The room specified here is the default room. The default is # The room specified here is the default room. The default is
# redundant in this case since a room must be specified. Leave empty. # redundant in this case since a room must be specified. Leave empty.
XML.SubElement(hippub, 'room').text = '' XML.SubElement(hippub, "room").text = ""

View File

@ -12,45 +12,20 @@
# Representation of the hudson.model.Result class # Representation of the hudson.model.Result class
SUCCESS = { SUCCESS = {"name": "SUCCESS", "ordinal": "0", "color": "BLUE", "complete": True}
'name': 'SUCCESS',
'ordinal': '0',
'color': 'BLUE',
'complete': True
}
UNSTABLE = { UNSTABLE = {"name": "UNSTABLE", "ordinal": "1", "color": "YELLOW", "complete": True}
'name': 'UNSTABLE',
'ordinal': '1',
'color': 'YELLOW',
'complete': True
}
FAILURE = { FAILURE = {"name": "FAILURE", "ordinal": "2", "color": "RED", "complete": True}
'name': 'FAILURE',
'ordinal': '2',
'color': 'RED',
'complete': True
}
NOTBUILD = { NOTBUILD = {"name": "NOT_BUILD", "ordinal": "3", "color": "NOTBUILD", "complete": False}
'name': 'NOT_BUILD',
'ordinal': '3',
'color': 'NOTBUILD',
'complete': False
}
ABORTED = { ABORTED = {"name": "ABORTED", "ordinal": "4", "color": "ABORTED", "complete": False}
'name': 'ABORTED',
'ordinal': '4',
'color': 'ABORTED',
'complete': False
}
THRESHOLDS = { THRESHOLDS = {
'SUCCESS': SUCCESS, "SUCCESS": SUCCESS,
'UNSTABLE': UNSTABLE, "UNSTABLE": UNSTABLE,
'FAILURE': FAILURE, "FAILURE": FAILURE,
'NOT_BUILD': NOTBUILD, "NOT_BUILD": NOTBUILD,
'ABORTED': ABORTED "ABORTED": ABORTED,
} }

View File

@ -38,13 +38,14 @@ import jenkins_jobs.modules.base
def base_metadata(registry, xml_parent, data, mtype): def base_metadata(registry, xml_parent, data, mtype):
pdef = XML.SubElement(xml_parent, mtype) pdef = XML.SubElement(xml_parent, mtype)
XML.SubElement(pdef, 'name').text = data['name'] XML.SubElement(pdef, "name").text = data["name"]
XML.SubElement(pdef, 'generated').text = 'false' XML.SubElement(pdef, "generated").text = "false"
XML.SubElement(pdef, 'parent', attrib={"class": "job-metadata", XML.SubElement(
"reference": "../../.."}) pdef, "parent", attrib={"class": "job-metadata", "reference": "../../.."}
)
exposed_to_env = XML.SubElement(pdef, 'exposedToEnvironment') exposed_to_env = XML.SubElement(pdef, "exposedToEnvironment")
exposed_to_env.text = str(data.get('expose-to-env', False)).lower() exposed_to_env.text = str(data.get("expose-to-env", False)).lower()
return pdef return pdef
@ -64,10 +65,9 @@ def string_metadata(registry, xml_parent, data):
value: bar value: bar
expose-to-env: true expose-to-env: true
""" """
pdef = base_metadata(registry, xml_parent, data, pdef = base_metadata(registry, xml_parent, data, "metadata-string")
'metadata-string') value = data.get("value", "")
value = data.get('value', '') XML.SubElement(pdef, "value").text = value
XML.SubElement(pdef, 'value').text = value
def number_metadata(registry, xml_parent, data): def number_metadata(registry, xml_parent, data):
@ -86,10 +86,9 @@ def number_metadata(registry, xml_parent, data):
value: 1 value: 1
expose-to-env: true expose-to-env: true
""" """
pdef = base_metadata(registry, xml_parent, data, pdef = base_metadata(registry, xml_parent, data, "metadata-number")
'metadata-number') value = data.get("value", "")
value = data.get('value', '') XML.SubElement(pdef, "value").text = value
XML.SubElement(pdef, 'value').text = value
def date_metadata(registry, xml_parent, data): def date_metadata(registry, xml_parent, data):
@ -110,30 +109,28 @@ def date_metadata(registry, xml_parent, data):
timezone: Australia/Melbourne timezone: Australia/Melbourne
expose-to-env: true expose-to-env: true
""" """
pdef = base_metadata(registry, xml_parent, data, pdef = base_metadata(registry, xml_parent, data, "metadata-date")
'metadata-date')
# TODO: convert time from any reasonable format into epoch # TODO: convert time from any reasonable format into epoch
mval = XML.SubElement(pdef, 'value') mval = XML.SubElement(pdef, "value")
XML.SubElement(mval, 'time').text = data['time'] XML.SubElement(mval, "time").text = data["time"]
XML.SubElement(mval, 'timezone').text = data['timezone'] XML.SubElement(mval, "timezone").text = data["timezone"]
XML.SubElement(pdef, 'checked').text = 'true' XML.SubElement(pdef, "checked").text = "true"
class Metadata(jenkins_jobs.modules.base.Base): class Metadata(jenkins_jobs.modules.base.Base):
sequence = 21 sequence = 21
component_type = 'metadata' component_type = "metadata"
component_list_type = 'metadata' component_list_type = "metadata"
def gen_xml(self, xml_parent, data): def gen_xml(self, xml_parent, data):
properties = xml_parent.find('properties') properties = xml_parent.find("properties")
if properties is None: if properties is None:
properties = XML.SubElement(xml_parent, 'properties') properties = XML.SubElement(xml_parent, "properties")
metadata = data.get('metadata', []) metadata = data.get("metadata", [])
if metadata: if metadata:
pdefp = XML.SubElement(properties, pdefp = XML.SubElement(properties, "job-metadata", plugin="metadata@1.0b")
'job-metadata', plugin="metadata@1.0b") pdefs = XML.SubElement(pdefp, "values")
pdefs = XML.SubElement(pdefp, 'values')
for mdata in metadata: for mdata in metadata:
self.registry.dispatch('metadata', pdefs, mdata) self.registry.dispatch("metadata", pdefs, mdata)

View File

@ -52,44 +52,44 @@ def http_endpoint(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
endpoint_element = XML.SubElement(xml_parent, endpoint_element = XML.SubElement(
'com.tikal.hudson.plugins.notification.' xml_parent, "com.tikal.hudson.plugins.notification." "Endpoint"
'Endpoint') )
supported_formats = ['JSON', 'XML'] supported_formats = ["JSON", "XML"]
supported_events = ['started', 'completed', 'finalized', 'all'] supported_events = ["started", "completed", "finalized", "all"]
fmt = data.get('format', 'JSON').upper() fmt = data.get("format", "JSON").upper()
event = data.get('event', 'all').lower() event = data.get("event", "all").lower()
mapping = [ mapping = [
('', 'format', fmt, supported_formats), ("", "format", fmt, supported_formats),
('', 'protocol', 'HTTP'), ("", "protocol", "HTTP"),
('', 'event', event, supported_events), ("", "event", event, supported_events),
('timeout', 'timeout', 30000), ("timeout", "timeout", 30000),
('url', 'url', None), ("url", "url", None),
('log', 'loglines', 0), ("log", "loglines", 0),
] ]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(endpoint_element, data, mapping, fail_required=True)
endpoint_element, data, mapping, fail_required=True)
class Notifications(jenkins_jobs.modules.base.Base): class Notifications(jenkins_jobs.modules.base.Base):
sequence = 22 sequence = 22
component_type = 'notification' component_type = "notification"
component_list_type = 'notifications' component_list_type = "notifications"
def gen_xml(self, xml_parent, data): def gen_xml(self, xml_parent, data):
properties = xml_parent.find('properties') properties = xml_parent.find("properties")
if properties is None: if properties is None:
properties = XML.SubElement(xml_parent, 'properties') properties = XML.SubElement(xml_parent, "properties")
notifications = data.get('notifications', []) notifications = data.get("notifications", [])
if notifications: if notifications:
notify_element = XML.SubElement(properties, notify_element = XML.SubElement(
'com.tikal.hudson.plugins.' properties,
'notification.' "com.tikal.hudson.plugins."
'HudsonNotificationProperty') "notification."
endpoints_element = XML.SubElement(notify_element, 'endpoints') "HudsonNotificationProperty",
)
endpoints_element = XML.SubElement(notify_element, "endpoints")
for endpoint in notifications: for endpoint in notifications:
self.registry.dispatch('notification', self.registry.dispatch("notification", endpoints_element, endpoint)
endpoints_element, endpoint)

View File

@ -43,14 +43,14 @@ import jenkins_jobs.modules.helpers as helpers
def base_param(registry, xml_parent, data, do_default, ptype): def base_param(registry, xml_parent, data, do_default, ptype):
pdef = XML.SubElement(xml_parent, ptype) pdef = XML.SubElement(xml_parent, ptype)
XML.SubElement(pdef, 'name').text = data['name'] XML.SubElement(pdef, "name").text = data["name"]
XML.SubElement(pdef, 'description').text = data.get('description', '') XML.SubElement(pdef, "description").text = data.get("description", "")
if do_default: if do_default:
default = data.get('default', None) default = data.get("default", None)
if default is not None: if default is not None:
XML.SubElement(pdef, 'defaultValue').text = str(default) XML.SubElement(pdef, "defaultValue").text = str(default)
else: else:
XML.SubElement(pdef, 'defaultValue') XML.SubElement(pdef, "defaultValue")
return pdef return pdef
@ -70,8 +70,9 @@ def string_param(registry, xml_parent, data):
default: bar default: bar
description: "A parameter named FOO, defaults to 'bar'." description: "A parameter named FOO, defaults to 'bar'."
""" """
base_param(registry, xml_parent, data, True, base_param(
'hudson.model.StringParameterDefinition') registry, xml_parent, data, True, "hudson.model.StringParameterDefinition"
)
def promoted_param(registry, xml_parent, data): def promoted_param(registry, xml_parent, data):
@ -92,16 +93,20 @@ def promoted_param(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
pdef = base_param(registry, xml_parent, data, False, pdef = base_param(
'hudson.plugins.promoted__builds.parameters.' registry,
'PromotedBuildParameterDefinition') xml_parent,
data,
False,
"hudson.plugins.promoted__builds.parameters."
"PromotedBuildParameterDefinition",
)
try: try:
XML.SubElement(pdef, 'projectName').text = data['project-name'] XML.SubElement(pdef, "projectName").text = data["project-name"]
except KeyError: except KeyError:
raise MissingAttributeError('project-name') raise MissingAttributeError("project-name")
XML.SubElement(pdef, 'promotionProcessName').text = data.get( XML.SubElement(pdef, "promotionProcessName").text = data.get("promotion-name", None)
'promotion-name', None)
def password_param(registry, xml_parent, data): def password_param(registry, xml_parent, data):
@ -120,8 +125,9 @@ def password_param(registry, xml_parent, data):
default: 1HSC0Ts6E161FysGf+e1xasgsHkgleLh09JUTYnipPvw= default: 1HSC0Ts6E161FysGf+e1xasgsHkgleLh09JUTYnipPvw=
description: "A parameter named FOO." description: "A parameter named FOO."
""" """
base_param(registry, xml_parent, data, True, base_param(
'hudson.model.PasswordParameterDefinition') registry, xml_parent, data, True, "hudson.model.PasswordParameterDefinition"
)
def bool_param(registry, xml_parent, data): def bool_param(registry, xml_parent, data):
@ -140,9 +146,10 @@ def bool_param(registry, xml_parent, data):
default: false default: false
description: "A parameter named FOO, defaults to 'false'." description: "A parameter named FOO, defaults to 'false'."
""" """
data['default'] = str(data.get('default', False)).lower() data["default"] = str(data.get("default", False)).lower()
base_param(registry, xml_parent, data, True, base_param(
'hudson.model.BooleanParameterDefinition') registry, xml_parent, data, True, "hudson.model.BooleanParameterDefinition"
)
def file_param(registry, xml_parent, data): def file_param(registry, xml_parent, data):
@ -159,8 +166,9 @@ def file_param(registry, xml_parent, data):
name: test.txt name: test.txt
description: "Upload test.txt." description: "Upload test.txt."
""" """
base_param(registry, xml_parent, data, False, base_param(
'hudson.model.FileParameterDefinition') registry, xml_parent, data, False, "hudson.model.FileParameterDefinition"
)
def text_param(registry, xml_parent, data): def text_param(registry, xml_parent, data):
@ -179,8 +187,7 @@ def text_param(registry, xml_parent, data):
default: bar default: bar
description: "A parameter named FOO, defaults to 'bar'." description: "A parameter named FOO, defaults to 'bar'."
""" """
base_param(registry, xml_parent, data, True, base_param(registry, xml_parent, data, True, "hudson.model.TextParameterDefinition")
'hudson.model.TextParameterDefinition')
def label_param(registry, xml_parent, data): def label_param(registry, xml_parent, data):
@ -204,35 +211,41 @@ def label_param(registry, xml_parent, data):
""" """
pdef = base_param(registry, xml_parent, data, True, pdef = base_param(
'org.jvnet.jenkins.plugins.nodelabelparameter.' registry,
'LabelParameterDefinition') xml_parent,
data,
True,
"org.jvnet.jenkins.plugins.nodelabelparameter." "LabelParameterDefinition",
)
valid_types = ['allCases', 'success', 'unstable'] valid_types = ["allCases", "success", "unstable"]
mapping = [ mapping = [
('all-nodes', 'allNodesMatchingLabel', False), ("all-nodes", "allNodesMatchingLabel", False),
('matching-label', 'triggerIfResult', 'allCases', valid_types), ("matching-label", "triggerIfResult", "allCases", valid_types),
] ]
helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True)
eligibility_label = data.get('node-eligibility', 'all').lower() eligibility_label = data.get("node-eligibility", "all").lower()
eligibility_label_dict = { eligibility_label_dict = {
'all': 'org.jvnet.jenkins.plugins.' "all": "org.jvnet.jenkins.plugins."
'nodelabelparameter.node.' "nodelabelparameter.node."
'AllNodeEligibility', "AllNodeEligibility",
'ignore-offline': 'org.jvnet.jenkins.plugins.' "ignore-offline": "org.jvnet.jenkins.plugins."
'nodelabelparameter.node.' "nodelabelparameter.node."
'IgnoreOfflineNodeEligibility', "IgnoreOfflineNodeEligibility",
'ignore-temp-offline': 'org.jvnet.jenkins.plugins.' "ignore-temp-offline": "org.jvnet.jenkins.plugins."
'nodelabelparameter.node.' "nodelabelparameter.node."
'IgnoreTempOfflineNodeEligibility', "IgnoreTempOfflineNodeEligibility",
} }
if eligibility_label not in eligibility_label_dict: if eligibility_label not in eligibility_label_dict:
raise InvalidAttributeError(eligibility_label, eligibility_label, raise InvalidAttributeError(
eligibility_label_dict.keys()) eligibility_label, eligibility_label, eligibility_label_dict.keys()
)
XML.SubElement(pdef, 'nodeEligibility').set( XML.SubElement(pdef, "nodeEligibility").set(
"class", eligibility_label_dict[eligibility_label]) "class", eligibility_label_dict[eligibility_label]
)
def node_param(registry, xml_parent, data): def node_param(registry, xml_parent, data):
@ -263,30 +276,37 @@ def node_param(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
pdef = base_param(registry, xml_parent, data, False, pdef = base_param(
'org.jvnet.jenkins.plugins.nodelabelparameter.' registry,
'NodeParameterDefinition') xml_parent,
default = XML.SubElement(pdef, 'defaultSlaves') data,
if 'default-slaves' in data: False,
for slave in data['default-slaves']: "org.jvnet.jenkins.plugins.nodelabelparameter." "NodeParameterDefinition",
XML.SubElement(default, 'string').text = slave )
allowed = XML.SubElement(pdef, 'allowedSlaves') default = XML.SubElement(pdef, "defaultSlaves")
if 'allowed-slaves' in data: if "default-slaves" in data:
for slave in data['allowed-slaves']: for slave in data["default-slaves"]:
XML.SubElement(allowed, 'string').text = slave XML.SubElement(default, "string").text = slave
XML.SubElement(pdef, 'ignoreOfflineNodes').text = str( allowed = XML.SubElement(pdef, "allowedSlaves")
data.get('ignore-offline-nodes', False)).lower() if "allowed-slaves" in data:
for slave in data["allowed-slaves"]:
XML.SubElement(allowed, "string").text = slave
XML.SubElement(pdef, "ignoreOfflineNodes").text = str(
data.get("ignore-offline-nodes", False)
).lower()
if data.get('allowed-multiselect', False): if data.get("allowed-multiselect", False):
XML.SubElement(pdef, 'triggerIfResult').text = \ XML.SubElement(
'allowMultiSelectionForConcurrentBuilds' pdef, "triggerIfResult"
).text = "allowMultiSelectionForConcurrentBuilds"
else: else:
XML.SubElement(pdef, 'triggerIfResult').text = \ XML.SubElement(pdef, "triggerIfResult").text = "multiSelectionDisallowed"
'multiSelectionDisallowed' XML.SubElement(pdef, "allowMultiNodeSelection").text = str(
XML.SubElement(pdef, 'allowMultiNodeSelection').text = str( data.get("allowed-multiselect", False)
data.get('allowed-multiselect', False)).lower() ).lower()
XML.SubElement(pdef, 'triggerConcurrentBuilds').text = str( XML.SubElement(pdef, "triggerConcurrentBuilds").text = str(
data.get('allowed-multiselect', False)).lower() data.get("allowed-multiselect", False)
).lower()
def choice_param(registry, xml_parent, data): def choice_param(registry, xml_parent, data):
@ -307,13 +327,13 @@ def choice_param(registry, xml_parent, data):
- glance - glance
description: "On which project to run?" description: "On which project to run?"
""" """
pdef = base_param(registry, xml_parent, data, False, pdef = base_param(
'hudson.model.ChoiceParameterDefinition') registry, xml_parent, data, False, "hudson.model.ChoiceParameterDefinition"
choices = XML.SubElement(pdef, 'choices', )
{'class': 'java.util.Arrays$ArrayList'}) choices = XML.SubElement(pdef, "choices", {"class": "java.util.Arrays$ArrayList"})
a = XML.SubElement(choices, 'a', {'class': 'string-array'}) a = XML.SubElement(choices, "a", {"class": "string-array"})
for choice in data['choices']: for choice in data["choices"]:
XML.SubElement(a, 'string').text = choice XML.SubElement(a, "string").text = choice
def credentials_param(registry, xml_parent, data): def credentials_param(registry, xml_parent, data):
@ -345,30 +365,33 @@ def credentials_param(registry, xml_parent, data):
""" """
cred_impl_types = { cred_impl_types = {
'any': 'com.cloudbees.plugins.credentials.common.StandardCredentials', "any": "com.cloudbees.plugins.credentials.common.StandardCredentials",
'usernamepassword': 'com.cloudbees.plugins.credentials.impl.' + "usernamepassword": "com.cloudbees.plugins.credentials.impl."
'UsernamePasswordCredentialsImpl', + "UsernamePasswordCredentialsImpl",
'sshkey': 'com.cloudbees.jenkins.plugins.sshcredentials.impl.' + "sshkey": "com.cloudbees.jenkins.plugins.sshcredentials.impl."
'BasicSSHUserPrivateKey', + "BasicSSHUserPrivateKey",
'secretfile': 'org.jenkinsci.plugins.plaincredentials.impl.' + "secretfile": "org.jenkinsci.plugins.plaincredentials.impl."
'FileCredentialsImpl', + "FileCredentialsImpl",
'secrettext': 'org.jenkinsci.plugins.plaincredentials.impl.' + "secrettext": "org.jenkinsci.plugins.plaincredentials.impl."
'StringCredentialsImpl', + "StringCredentialsImpl",
'certificate': 'com.cloudbees.plugins.credentials.impl.' + "certificate": "com.cloudbees.plugins.credentials.impl."
'CertificateCredentialsImpl' + "CertificateCredentialsImpl",
} }
cred_type = data.get('type', 'any').lower() cred_type = data.get("type", "any").lower()
if cred_type not in cred_impl_types: if cred_type not in cred_impl_types:
raise InvalidAttributeError('type', cred_type, cred_impl_types.keys()) raise InvalidAttributeError("type", cred_type, cred_impl_types.keys())
pdef = base_param(registry, xml_parent, data, False, pdef = base_param(
'com.cloudbees.plugins.credentials.' + registry,
'CredentialsParameterDefinition') xml_parent,
XML.SubElement(pdef, 'defaultValue').text = data.get('default', '') data,
XML.SubElement(pdef, 'credentialType').text = cred_impl_types[cred_type] False,
XML.SubElement(pdef, 'required').text = str(data.get('required', "com.cloudbees.plugins.credentials." + "CredentialsParameterDefinition",
False)).lower() )
XML.SubElement(pdef, "defaultValue").text = data.get("default", "")
XML.SubElement(pdef, "credentialType").text = cred_impl_types[cred_type]
XML.SubElement(pdef, "required").text = str(data.get("required", False)).lower()
def run_param(registry, xml_parent, data): def run_param(registry, xml_parent, data):
@ -385,11 +408,10 @@ def run_param(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
pdef = base_param(registry, xml_parent, data, False, pdef = base_param(
'hudson.model.RunParameterDefinition') registry, xml_parent, data, False, "hudson.model.RunParameterDefinition"
mapping = [ )
('project-name', 'projectName', None), mapping = [("project-name", "projectName", None)]
]
helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True)
@ -456,43 +478,50 @@ def extended_choice_param(registry, xml_parent, data):
/../../tests/parameters/fixtures/extended-choice-param-full.yaml /../../tests/parameters/fixtures/extended-choice-param-full.yaml
:language: yaml :language: yaml
""" """
pdef = base_param(registry, xml_parent, data, False, pdef = base_param(
'com.cwctravel.hudson.plugins.' registry,
'extended__choice__parameter.' xml_parent,
'ExtendedChoiceParameterDefinition') data,
False,
"com.cwctravel.hudson.plugins."
"extended__choice__parameter."
"ExtendedChoiceParameterDefinition",
)
choicedict = {'single-select': 'PT_SINGLE_SELECT', choicedict = {
'multi-select': 'PT_MULTI_SELECT', "single-select": "PT_SINGLE_SELECT",
'radio': 'PT_RADIO', "multi-select": "PT_MULTI_SELECT",
'checkbox': 'PT_CHECKBOX', "radio": "PT_RADIO",
'textbox': 'PT_TEXTBOX', "checkbox": "PT_CHECKBOX",
'PT_SINGLE_SELECT': 'PT_SINGLE_SELECT', "textbox": "PT_TEXTBOX",
'PT_MULTI_SELECT': 'PT_MULTI_SELECT', "PT_SINGLE_SELECT": "PT_SINGLE_SELECT",
'PT_RADIO': 'PT_RADIO', "PT_MULTI_SELECT": "PT_MULTI_SELECT",
'PT_CHECKBOX': 'PT_CHECKBOX', "PT_RADIO": "PT_RADIO",
'PT_TEXTBOX': 'PT_TEXTBOX'} "PT_CHECKBOX": "PT_CHECKBOX",
"PT_TEXTBOX": "PT_TEXTBOX",
}
mapping = [ mapping = [
('value', 'value', ''), ("value", "value", ""),
('visible-items', 'visibleItemCount', 5), ("visible-items", "visibleItemCount", 5),
('multi-select-delimiter', 'multiSelectDelimiter', ','), ("multi-select-delimiter", "multiSelectDelimiter", ","),
('quote-value', 'quoteValue', False), ("quote-value", "quoteValue", False),
('default-value', 'defaultValue', ''), ("default-value", "defaultValue", ""),
('value-description', 'descriptionPropertyValue', ''), ("value-description", "descriptionPropertyValue", ""),
('type', 'type', 'single-select', choicedict), ("type", "type", "single-select", choicedict),
('property-file', 'propertyFile', ''), ("property-file", "propertyFile", ""),
('property-key', 'propertyKey', ''), ("property-key", "propertyKey", ""),
('default-property-file', 'defaultPropertyFile', ''), ("default-property-file", "defaultPropertyFile", ""),
('default-property-key', 'defaultPropertyKey', ''), ("default-property-key", "defaultPropertyKey", ""),
('description-property-file', 'descriptionPropertyFile', ''), ("description-property-file", "descriptionPropertyFile", ""),
('description-property-key', 'descriptionPropertyKey', ''), ("description-property-key", "descriptionPropertyKey", ""),
('bindings', 'bindings', ''), ("bindings", "bindings", ""),
('groovy-script', 'groovyScript', ''), ("groovy-script", "groovyScript", ""),
('groovy-script-file', 'groovyScriptFile', ''), ("groovy-script-file", "groovyScriptFile", ""),
('classpath', 'groovyClasspath', ''), ("classpath", "groovyClasspath", ""),
('default-groovy-script', 'defaultGroovyScript', ''), ("default-groovy-script", "defaultGroovyScript", ""),
('default-groovy-classpath', 'defaultGroovyClasspath', ''), ("default-groovy-classpath", "defaultGroovyClasspath", ""),
('description-groovy-script', 'descriptionGroovyScript', ''), ("description-groovy-script", "descriptionGroovyScript", ""),
('description-groovy-classpath', 'descriptionGroovyClasspath', ''), ("description-groovy-classpath", "descriptionGroovyClasspath", ""),
] ]
helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True)
@ -519,13 +548,15 @@ def validating_string_param(registry, xml_parent, data):
regex: [A-Za-z]* regex: [A-Za-z]*
msg: Your entered value failed validation msg: Your entered value failed validation
""" """
pdef = base_param(registry, xml_parent, data, True, pdef = base_param(
'hudson.plugins.validating__string__parameter.' registry,
'ValidatingStringParameterDefinition') xml_parent,
mapping = [ data,
('regex', 'regex', None), True,
('msg', 'failedValidationMessage', None), "hudson.plugins.validating__string__parameter."
] "ValidatingStringParameterDefinition",
)
mapping = [("regex", "regex", None), ("msg", "failedValidationMessage", None)]
helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True)
@ -557,17 +588,21 @@ def svn_tags_param(registry, xml_parent, data):
url: http://svn.example.com/repo url: http://svn.example.com/repo
filter: [A-za-z0-9]* filter: [A-za-z0-9]*
""" """
pdef = base_param(registry, xml_parent, data, True, pdef = base_param(
'hudson.scm.listtagsparameter.' registry,
'ListSubversionTagsParameterDefinition') xml_parent,
data,
True,
"hudson.scm.listtagsparameter." "ListSubversionTagsParameterDefinition",
)
mapping = [ mapping = [
('url', 'tagsDir', None), ("url", "tagsDir", None),
('credentials-id', 'credentialsId', ''), ("credentials-id", "credentialsId", ""),
('filter', 'tagsFilter', ''), ("filter", "tagsFilter", ""),
('max-tags', 'maxTags', '100'), ("max-tags", "maxTags", "100"),
('sort-newest-first', 'reverseByDate', True), ("sort-newest-first", "reverseByDate", True),
('sort-z-to-a', 'reverseByName', False), ("sort-z-to-a", "reverseByName", False),
('', 'uuid', "1-1-1-1-1"), ("", "uuid", "1-1-1-1-1"),
] ]
helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True)
@ -597,8 +632,7 @@ def dynamic_choice_param(registry, xml_parent, data):
remote: false remote: false
read-only: false read-only: false
""" """
dynamic_param_common(registry, xml_parent, data, dynamic_param_common(registry, xml_parent, data, "ChoiceParameterDefinition")
'ChoiceParameterDefinition')
def dynamic_string_param(registry, xml_parent, data): def dynamic_string_param(registry, xml_parent, data):
@ -626,8 +660,7 @@ def dynamic_string_param(registry, xml_parent, data):
remote: false remote: false
read-only: false read-only: false
""" """
dynamic_param_common(registry, xml_parent, data, dynamic_param_common(registry, xml_parent, data, "StringParameterDefinition")
'StringParameterDefinition')
def dynamic_choice_scriptler_param(registry, xml_parent, data): def dynamic_choice_scriptler_param(registry, xml_parent, data):
@ -663,8 +696,9 @@ def dynamic_choice_scriptler_param(registry, xml_parent, data):
remote: false remote: false
read-only: false read-only: false
""" """
dynamic_scriptler_param_common(registry, xml_parent, data, dynamic_scriptler_param_common(
'ScriptlerChoiceParameterDefinition') registry, xml_parent, data, "ScriptlerChoiceParameterDefinition"
)
def dynamic_string_scriptler_param(registry, xml_parent, data): def dynamic_string_scriptler_param(registry, xml_parent, data):
@ -700,54 +734,64 @@ def dynamic_string_scriptler_param(registry, xml_parent, data):
remote: false remote: false
read-only: false read-only: false
""" """
dynamic_scriptler_param_common(registry, xml_parent, data, dynamic_scriptler_param_common(
'ScriptlerStringParameterDefinition') registry, xml_parent, data, "ScriptlerStringParameterDefinition"
)
def dynamic_param_common(registry, xml_parent, data, ptype): def dynamic_param_common(registry, xml_parent, data, ptype):
pdef = base_param(registry, xml_parent, data, False, pdef = base_param(
'com.seitenbau.jenkins.plugins.dynamicparameter.' + registry,
ptype) xml_parent,
XML.SubElement(pdef, '__remote').text = str( data,
data.get('remote', False)).lower() False,
XML.SubElement(pdef, '__script').text = data.get('script', None) "com.seitenbau.jenkins.plugins.dynamicparameter." + ptype,
localBaseDir = XML.SubElement(pdef, '__localBaseDirectory', )
{'serialization': 'custom'}) XML.SubElement(pdef, "__remote").text = str(data.get("remote", False)).lower()
filePath = XML.SubElement(localBaseDir, 'hudson.FilePath') XML.SubElement(pdef, "__script").text = data.get("script", None)
default = XML.SubElement(filePath, 'default') localBaseDir = XML.SubElement(
XML.SubElement(filePath, 'boolean').text = "true" pdef, "__localBaseDirectory", {"serialization": "custom"}
XML.SubElement(default, 'remote').text = \ )
"/var/lib/jenkins/dynamic_parameter/classpath" filePath = XML.SubElement(localBaseDir, "hudson.FilePath")
XML.SubElement(pdef, '__remoteBaseDirectory').text = \ default = XML.SubElement(filePath, "default")
"dynamic_parameter_classpath" XML.SubElement(filePath, "boolean").text = "true"
XML.SubElement(pdef, '__classPath').text = data.get('classpath', None) XML.SubElement(
XML.SubElement(pdef, 'readonlyInputField').text = str( default, "remote"
data.get('read-only', False)).lower() ).text = "/var/lib/jenkins/dynamic_parameter/classpath"
XML.SubElement(pdef, "__remoteBaseDirectory").text = "dynamic_parameter_classpath"
XML.SubElement(pdef, "__classPath").text = data.get("classpath", None)
XML.SubElement(pdef, "readonlyInputField").text = str(
data.get("read-only", False)
).lower()
def dynamic_scriptler_param_common(registry, xml_parent, data, ptype): def dynamic_scriptler_param_common(registry, xml_parent, data, ptype):
pdef = base_param(registry, xml_parent, data, False, pdef = base_param(
'com.seitenbau.jenkins.plugins.dynamicparameter.' registry,
'scriptler.' + ptype) xml_parent,
parametersXML = XML.SubElement(pdef, '__parameters') data,
parameters = data.get('parameters', []) False,
"com.seitenbau.jenkins.plugins.dynamicparameter." "scriptler." + ptype,
)
parametersXML = XML.SubElement(pdef, "__parameters")
parameters = data.get("parameters", [])
if parameters: if parameters:
mapping = [ mapping = [("name", "name", None), ("value", "value", None)]
('name', 'name', None),
('value', 'value', None),
]
for parameter in parameters: for parameter in parameters:
parameterXML = XML.SubElement(parametersXML, parameterXML = XML.SubElement(
'com.seitenbau.jenkins.plugins.' parametersXML,
'dynamicparameter.scriptler.' "com.seitenbau.jenkins.plugins."
'ScriptlerParameterDefinition_' "dynamicparameter.scriptler."
'-ScriptParameter') "ScriptlerParameterDefinition_"
"-ScriptParameter",
)
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(
parameterXML, parameter, mapping, fail_required=True) parameterXML, parameter, mapping, fail_required=True
)
mapping = [ mapping = [
('script-id', '__scriptlerScriptId', None), ("script-id", "__scriptlerScriptId", None),
('remote', '__remote', False), ("remote", "__remote", False),
('read-only', 'readonlyInputField', False), ("read-only", "readonlyInputField", False),
] ]
helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True)
@ -770,14 +814,16 @@ def matrix_combinations_param(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
element_name = 'hudson.plugins.matrix__configuration__parameter.' \ element_name = (
'MatrixCombinationsParameterDefinition' "hudson.plugins.matrix__configuration__parameter."
"MatrixCombinationsParameterDefinition"
)
pdef = XML.SubElement(xml_parent, element_name) pdef = XML.SubElement(xml_parent, element_name)
mapping = [ mapping = [
('name', 'name', None), ("name", "name", None),
('description', 'description', ''), ("description", "description", ""),
('filter', 'defaultCombinationFilter', ''), ("filter", "defaultCombinationFilter", ""),
] ]
helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True)
@ -806,15 +852,13 @@ def copyartifact_build_selector_param(registry, xml_parent, data):
""" """
t = XML.SubElement(xml_parent, 'hudson.plugins.copyartifact.' t = XML.SubElement(
'BuildSelectorParameter') xml_parent, "hudson.plugins.copyartifact." "BuildSelectorParameter"
mapping = [ )
('name', 'name', None), mapping = [("name", "name", None), ("description", "description", "")]
('description', 'description', ''),
]
helpers.convert_mapping_to_xml(t, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(t, data, mapping, fail_required=True)
helpers.copyartifact_build_selector(t, data, 'defaultSelector') helpers.copyartifact_build_selector(t, data, "defaultSelector")
def maven_metadata_param(registry, xml_parent, data): def maven_metadata_param(registry, xml_parent, data):
@ -855,31 +899,34 @@ def maven_metadata_param(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
pdef = base_param(registry, xml_parent, data, False, pdef = base_param(
'eu.markov.jenkins.plugin.mvnmeta.' registry,
'MavenMetadataParameterDefinition') xml_parent,
data,
False,
"eu.markov.jenkins.plugin.mvnmeta." "MavenMetadataParameterDefinition",
)
mapping = [ mapping = [
('repository-base-url', 'repoBaseUrl', ''), ("repository-base-url", "repoBaseUrl", ""),
('artifact-group-id', 'groupId', ''), ("artifact-group-id", "groupId", ""),
('artifact-id', 'artifactId', ''), ("artifact-id", "artifactId", ""),
('packaging', 'packaging', ''), ("packaging", "packaging", ""),
('default-value', 'defaultValue', ''), ("default-value", "defaultValue", ""),
('versions-filter', 'versionFilter', ''), ("versions-filter", "versionFilter", ""),
] ]
helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True)
sort_order = data.get('sorting-order', 'descending').lower() sort_order = data.get("sorting-order", "descending").lower()
sort_dict = {'descending': 'DESC', sort_dict = {"descending": "DESC", "ascending": "ASC"}
'ascending': 'ASC'}
if sort_order not in sort_dict: if sort_order not in sort_dict:
raise InvalidAttributeError(sort_order, sort_order, sort_dict.keys()) raise InvalidAttributeError(sort_order, sort_order, sort_dict.keys())
XML.SubElement(pdef, 'sortOrder').text = sort_dict[sort_order] XML.SubElement(pdef, "sortOrder").text = sort_dict[sort_order]
mapping = [ mapping = [
('maximum-versions-to-display', 'maxVersions', 10), ("maximum-versions-to-display", "maxVersions", 10),
('repository-username', 'username', ''), ("repository-username", "username", ""),
('repository-password', 'password', ''), ("repository-password", "password", ""),
] ]
helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True)
@ -901,8 +948,9 @@ def hidden_param(parser, xml_parent, data):
:language: yaml :language: yaml
""" """
base_param(parser, xml_parent, data, True, base_param(
'com.wangyin.parameter.WHideParameterDefinition') parser, xml_parent, data, True, "com.wangyin.parameter.WHideParameterDefinition"
)
def random_string_param(registry, xml_parent, data): def random_string_param(registry, xml_parent, data):
@ -923,16 +971,17 @@ def random_string_param(registry, xml_parent, data):
/../../tests/parameters/fixtures/random-string-param001.yaml /../../tests/parameters/fixtures/random-string-param001.yaml
:language: yaml :language: yaml
""" """
pdef = XML.SubElement(xml_parent, pdef = XML.SubElement(
'hudson.plugins.random__string__parameter.' xml_parent,
'RandomStringParameterDefinition') "hudson.plugins.random__string__parameter." "RandomStringParameterDefinition",
if 'name' not in data: )
raise JenkinsJobsException('random-string must have a name parameter.') if "name" not in data:
raise JenkinsJobsException("random-string must have a name parameter.")
mapping = [ mapping = [
('name', 'name', None), ("name", "name", None),
('description', 'description', ''), ("description", "description", ""),
('failed-validation-message', 'failedValidationMessage', ''), ("failed-validation-message", "failedValidationMessage", ""),
] ]
helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True)
@ -1005,40 +1054,41 @@ def git_parameter_param(registry, xml_parent, data):
/../../tests/parameters/fixtures/git-parameter-param-full.yaml /../../tests/parameters/fixtures/git-parameter-param-full.yaml
:language: yaml :language: yaml
""" """
pdef = XML.SubElement(xml_parent, pdef = XML.SubElement(
'net.uaznia.lukanus.hudson.plugins.gitparameter.' xml_parent,
'GitParameterDefinition') "net.uaznia.lukanus.hudson.plugins.gitparameter." "GitParameterDefinition",
)
valid_types = [ valid_types = [
'PT_TAG', "PT_TAG",
'PT_BRANCH', "PT_BRANCH",
'PT_BRANCH_TAG', "PT_BRANCH_TAG",
'PT_REVISION', "PT_REVISION",
'PT_PULL_REQUEST', "PT_PULL_REQUEST",
] ]
valid_sort_modes = [ valid_sort_modes = [
'NONE', "NONE",
'ASCENDING', "ASCENDING",
'ASCENDING_SMART', "ASCENDING_SMART",
'DESCENDING', "DESCENDING",
'DESCENDING_SMART', "DESCENDING_SMART",
] ]
valid_selected_values = ['NONE', 'TOP', 'DEFAULT'] valid_selected_values = ["NONE", "TOP", "DEFAULT"]
mapping = [ mapping = [
('name', 'name', None), ("name", "name", None),
('description', 'description', ''), ("description", "description", ""),
('type', 'type', 'PT_TAG', valid_types), ("type", "type", "PT_TAG", valid_types),
('branch', 'branch', ''), ("branch", "branch", ""),
('tagFilter', 'tagFilter', '*'), ("tagFilter", "tagFilter", "*"),
('branchFilter', 'branchFilter', '.*'), ("branchFilter", "branchFilter", ".*"),
('sortMode', 'sortMode', 'NONE', valid_sort_modes), ("sortMode", "sortMode", "NONE", valid_sort_modes),
('defaultValue', 'defaultValue', ''), ("defaultValue", "defaultValue", ""),
('selectedValue', 'selectedValue', 'NONE', valid_selected_values), ("selectedValue", "selectedValue", "NONE", valid_selected_values),
('useRepository', 'useRepository', ''), ("useRepository", "useRepository", ""),
('quickFilterEnabled', 'quickFilterEnabled', False), ("quickFilterEnabled", "quickFilterEnabled", False),
] ]
helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(pdef, data, mapping, fail_required=True)
@ -1046,28 +1096,29 @@ def git_parameter_param(registry, xml_parent, data):
class Parameters(jenkins_jobs.modules.base.Base): class Parameters(jenkins_jobs.modules.base.Base):
sequence = 21 sequence = 21
component_type = 'parameter' component_type = "parameter"
component_list_type = 'parameters' component_list_type = "parameters"
def gen_xml(self, xml_parent, data): def gen_xml(self, xml_parent, data):
properties = xml_parent.find('properties') properties = xml_parent.find("properties")
if properties is None: if properties is None:
properties = XML.SubElement(xml_parent, 'properties') properties = XML.SubElement(xml_parent, "properties")
parameters = data.get('parameters', []) parameters = data.get("parameters", [])
hmodel = 'hudson.model.' hmodel = "hudson.model."
if parameters: if parameters:
# The conditionals here are to work around the extended_choice # The conditionals here are to work around the extended_choice
# parameter also being definable in the properties module. This # parameter also being definable in the properties module. This
# usage has been deprecated but not removed. Because it may have # usage has been deprecated but not removed. Because it may have
# added these elements before us, we need to check if they already # added these elements before us, we need to check if they already
# exist, and only add them if they're missing. # exist, and only add them if they're missing.
pdefp = properties.find(hmodel + 'ParametersDefinitionProperty') pdefp = properties.find(hmodel + "ParametersDefinitionProperty")
if pdefp is None: if pdefp is None:
pdefp = XML.SubElement(properties, pdefp = XML.SubElement(
hmodel + 'ParametersDefinitionProperty') properties, hmodel + "ParametersDefinitionProperty"
pdefs = pdefp.find('parameterDefinitions') )
pdefs = pdefp.find("parameterDefinitions")
if pdefs is None: if pdefs is None:
pdefs = XML.SubElement(pdefp, 'parameterDefinitions') pdefs = XML.SubElement(pdefp, "parameterDefinitions")
for param in parameters: for param in parameters:
self.registry.dispatch('parameter', pdefs, param) self.registry.dispatch("parameter", pdefs, param)

View File

@ -40,5 +40,5 @@ class ExternalJob(jenkins_jobs.modules.base.Base):
sequence = 0 sequence = 0
def root_xml(self, data): def root_xml(self, data):
xml_parent = XML.Element('hudson.model.ExternalJob') xml_parent = XML.Element("hudson.model.ExternalJob")
return xml_parent return xml_parent

View File

@ -59,15 +59,15 @@ class Flow(jenkins_jobs.modules.base.Base):
sequence = 0 sequence = 0
def root_xml(self, data): def root_xml(self, data):
xml_parent = XML.Element('com.cloudbees.plugins.flow.BuildFlow') xml_parent = XML.Element("com.cloudbees.plugins.flow.BuildFlow")
needs_workspace = data.get('needs-workspace', False) needs_workspace = data.get("needs-workspace", False)
mapping = [ mapping = [
('dsl', 'dsl', ''), ("dsl", "dsl", ""),
('needs-workspace', 'buildNeedsWorkspace', False), ("needs-workspace", "buildNeedsWorkspace", False),
] ]
convert_mapping_to_xml(xml_parent, data, mapping, fail_required=True) convert_mapping_to_xml(xml_parent, data, mapping, fail_required=True)
if needs_workspace and 'dsl-file' in data: if needs_workspace and "dsl-file" in data:
XML.SubElement(xml_parent, 'dslFile').text = data['dsl-file'] XML.SubElement(xml_parent, "dslFile").text = data["dsl-file"]
return xml_parent return xml_parent

View File

@ -43,19 +43,18 @@ class Folder(jenkins_jobs.modules.base.Base):
sequence = 0 sequence = 0
def root_xml(self, data): def root_xml(self, data):
xml_parent = XML.Element('com.cloudbees.hudson.plugins.folder.Folder', xml_parent = XML.Element(
plugin="cloudbees-folder") "com.cloudbees.hudson.plugins.folder.Folder", plugin="cloudbees-folder"
attributes = {"class": "com.cloudbees.hudson.plugins.folder." )
"icons.StockFolderIcon"} attributes = {
XML.SubElement(xml_parent, 'icon', attrib=attributes) "class": "com.cloudbees.hudson.plugins.folder." "icons.StockFolderIcon"
XML.SubElement(xml_parent, 'views') }
XML.SubElement(xml_parent, "icon", attrib=attributes)
XML.SubElement(xml_parent, "views")
attributes = {"class": "hudson.views.DefaultViewsTabBar"} attributes = {"class": "hudson.views.DefaultViewsTabBar"}
XML.SubElement(xml_parent, 'viewsTabBar', attrib=attributes) XML.SubElement(xml_parent, "viewsTabBar", attrib=attributes)
mappings = [ mappings = [("", "primaryView", "All"), ("", "healthMetrics", "")]
('', 'primaryView', 'All'),
('', 'healthMetrics', ''),
]
convert_mapping_to_xml(xml_parent, data, mappings, True) convert_mapping_to_xml(xml_parent, data, mappings, True)
return xml_parent return xml_parent

View File

@ -36,5 +36,5 @@ class Freestyle(jenkins_jobs.modules.base.Base):
sequence = 0 sequence = 0
def root_xml(self, data): def root_xml(self, data):
xml_parent = XML.Element('project') xml_parent = XML.Element("project")
return xml_parent return xml_parent

View File

@ -113,139 +113,144 @@ class Matrix(jenkins_jobs.modules.base.Base):
# List the supported Axis names in our configuration # List the supported Axis names in our configuration
# and map them to the Jenkins XML element name. # and map them to the Jenkins XML element name.
supported_axis = { supported_axis = {
'label-expression': 'hudson.matrix.LabelExpAxis', "label-expression": "hudson.matrix.LabelExpAxis",
'user-defined': 'hudson.matrix.TextAxis', "user-defined": "hudson.matrix.TextAxis",
'slave': 'hudson.matrix.LabelAxis', "slave": "hudson.matrix.LabelAxis",
'jdk': 'hudson.matrix.JDKAxis', "jdk": "hudson.matrix.JDKAxis",
'dynamic': 'ca.silvermaplesolutions.jenkins.plugins.daxis.DynamicAxis', "dynamic": "ca.silvermaplesolutions.jenkins.plugins.daxis.DynamicAxis",
'python': 'jenkins.plugins.shiningpanda.matrix.PythonAxis', "python": "jenkins.plugins.shiningpanda.matrix.PythonAxis",
'tox': 'jenkins.plugins.shiningpanda.matrix.ToxAxis', "tox": "jenkins.plugins.shiningpanda.matrix.ToxAxis",
'groovy': 'org.jenkinsci.plugins.GroovyAxis', "groovy": "org.jenkinsci.plugins.GroovyAxis",
'yaml': 'org.jenkinsci.plugins.yamlaxis.YamlAxis', "yaml": "org.jenkinsci.plugins.yamlaxis.YamlAxis",
} }
supported_strategies = { supported_strategies = {
# Jenkins built-in, default # Jenkins built-in, default
'execution-strategy': "execution-strategy": "hudson.matrix.DefaultMatrixExecutionStrategyImpl",
'hudson.matrix.DefaultMatrixExecutionStrategyImpl', "yaml-strategy": "org.jenkinsci.plugins.yamlaxis.YamlMatrixExecutionStrategy",
'yaml-strategy': "p4-strategy": "org.jenkinsci.plugins.p4.matrix.MatrixOptions",
'org.jenkinsci.plugins.yamlaxis.YamlMatrixExecutionStrategy',
'p4-strategy':
'org.jenkinsci.plugins.p4.matrix.MatrixOptions'
} }
def root_xml(self, data): def root_xml(self, data):
root = XML.Element('matrix-project') root = XML.Element("matrix-project")
# Default to 'execution-strategy' # Default to 'execution-strategy'
strategies = ([s for s in data.keys() if s.endswith('-strategy')] or strategies = [s for s in data.keys() if s.endswith("-strategy")] or [
['execution-strategy']) "execution-strategy"
]
# Job can not have multiple strategies # Job can not have multiple strategies
if len(strategies) > 1: if len(strategies) > 1:
raise ValueError( raise ValueError(
'matrix-project does not support multiple strategies. ' "matrix-project does not support multiple strategies. "
'Given %s: %s' % (len(strategies), ', '.join(strategies))) "Given %s: %s" % (len(strategies), ", ".join(strategies))
)
strategy_name = strategies[0] strategy_name = strategies[0]
if strategy_name not in self.supported_strategies: if strategy_name not in self.supported_strategies:
raise ValueError( raise ValueError(
'Given strategy %s. Only %s strategies are supported' "Given strategy %s. Only %s strategies are supported"
% (strategy_name, self.supported_strategies.keys())) % (strategy_name, self.supported_strategies.keys())
)
ex_r = XML.SubElement( ex_r = XML.SubElement(
root, 'executionStrategy', root,
{'class': self.supported_strategies[strategy_name]}) "executionStrategy",
{"class": self.supported_strategies[strategy_name]},
)
strategy = data.get(strategy_name, {}) strategy = data.get(strategy_name, {})
if strategy_name == 'execution-strategy': if strategy_name == "execution-strategy":
XML.SubElement(root, 'combinationFilter').text = ( XML.SubElement(root, "combinationFilter").text = str(
str(strategy.get('combination-filter', '')).rstrip() strategy.get("combination-filter", "")
) ).rstrip()
XML.SubElement(ex_r, 'runSequentially').text = ( XML.SubElement(ex_r, "runSequentially").text = str(
str(strategy.get('sequential', False)).lower() strategy.get("sequential", False)
) ).lower()
if 'touchstone' in strategy: if "touchstone" in strategy:
XML.SubElement(ex_r, 'touchStoneCombinationFilter').text = ( XML.SubElement(ex_r, "touchStoneCombinationFilter").text = str(
str(strategy['touchstone'].get('expr', '')) strategy["touchstone"].get("expr", "")
) )
threshold = strategy['touchstone'].get( threshold = strategy["touchstone"].get("result", "stable").upper()
'result', 'stable').upper() supported_thresholds = ("STABLE", "UNSTABLE")
supported_thresholds = ('STABLE', 'UNSTABLE')
if threshold not in supported_thresholds: if threshold not in supported_thresholds:
raise InvalidAttributeError( raise InvalidAttributeError(
'touchstone', threshold, supported_thresholds) "touchstone", threshold, supported_thresholds
)
# Web ui uses Stable but hudson.model.Result has Success # Web ui uses Stable but hudson.model.Result has Success
if threshold == 'STABLE': if threshold == "STABLE":
threshold = 'SUCCESS' threshold = "SUCCESS"
t_r = XML.SubElement(ex_r, 'touchStoneResultCondition') t_r = XML.SubElement(ex_r, "touchStoneResultCondition")
for sub_elem in ('name', 'ordinal', 'color'): for sub_elem in ("name", "ordinal", "color"):
XML.SubElement(t_r, sub_elem).text = ( XML.SubElement(t_r, sub_elem).text = hudson_model.THRESHOLDS[
hudson_model.THRESHOLDS[threshold][sub_elem]) threshold
][sub_elem]
elif strategy_name == 'yaml-strategy': elif strategy_name == "yaml-strategy":
filename = str(strategy.get('filename', '')) filename = str(strategy.get("filename", ""))
text = str(strategy.get('text', '')) text = str(strategy.get("text", ""))
exclude_key = str(strategy.get('exclude-key', '')) exclude_key = str(strategy.get("exclude-key", ""))
if bool(filename) == bool(text): # xor with str if bool(filename) == bool(text): # xor with str
raise ValueError('yaml-strategy must be given ' raise ValueError(
'either "filename" or "text"') "yaml-strategy must be given " 'either "filename" or "text"'
)
yamlType = (filename and 'file') or (text and 'text') yamlType = (filename and "file") or (text and "text")
XML.SubElement(ex_r, 'yamlType').text = yamlType XML.SubElement(ex_r, "yamlType").text = yamlType
XML.SubElement(ex_r, 'yamlFile').text = filename XML.SubElement(ex_r, "yamlFile").text = filename
XML.SubElement(ex_r, 'yamlText').text = text XML.SubElement(ex_r, "yamlText").text = text
XML.SubElement(ex_r, 'excludeKey').text = exclude_key XML.SubElement(ex_r, "excludeKey").text = exclude_key
elif strategy_name == 'p4-strategy': elif strategy_name == "p4-strategy":
XML.SubElement(ex_r, 'runSequentially').text = ( XML.SubElement(ex_r, "runSequentially").text = str(
str(strategy.get('sequential', False)).lower() strategy.get("sequential", False)
) ).lower()
XML.SubElement(ex_r, 'buildParent').text = ( XML.SubElement(ex_r, "buildParent").text = str(
str(strategy.get('build-parent', False)).lower() strategy.get("build-parent", False)
) ).lower()
ax_root = XML.SubElement(root, 'axes') ax_root = XML.SubElement(root, "axes")
for axis_ in data.get('axes', []): for axis_ in data.get("axes", []):
axis = axis_['axis'] axis = axis_["axis"]
axis_type = axis['type'] axis_type = axis["type"]
if axis_type not in self.supported_axis: if axis_type not in self.supported_axis:
raise ValueError('Only %s axes types are supported' raise ValueError(
% self.supported_axis.keys()) "Only %s axes types are supported" % self.supported_axis.keys()
)
axis_name = self.supported_axis.get(axis_type) axis_name = self.supported_axis.get(axis_type)
lbl_root = XML.SubElement(ax_root, axis_name) lbl_root = XML.SubElement(ax_root, axis_name)
name, values = axis.get('name', ''), axis.get('values', ['']) name, values = axis.get("name", ""), axis.get("values", [""])
if axis_type == 'jdk': if axis_type == "jdk":
XML.SubElement(lbl_root, 'name').text = 'jdk' XML.SubElement(lbl_root, "name").text = "jdk"
elif axis_type == 'python': elif axis_type == "python":
XML.SubElement(lbl_root, 'name').text = 'PYTHON' XML.SubElement(lbl_root, "name").text = "PYTHON"
elif axis_type == 'tox': elif axis_type == "tox":
XML.SubElement(lbl_root, 'name').text = 'TOXENV' XML.SubElement(lbl_root, "name").text = "TOXENV"
else: else:
XML.SubElement(lbl_root, 'name').text = str(name) XML.SubElement(lbl_root, "name").text = str(name)
if axis_type != "groovy": if axis_type != "groovy":
v_root = XML.SubElement(lbl_root, 'values') v_root = XML.SubElement(lbl_root, "values")
if axis_type == "dynamic": if axis_type == "dynamic":
XML.SubElement(v_root, 'string').text = str(values[0]) XML.SubElement(v_root, "string").text = str(values[0])
XML.SubElement(lbl_root, 'varName').text = str(values[0]) XML.SubElement(lbl_root, "varName").text = str(values[0])
v_root = XML.SubElement(lbl_root, 'axisValues') v_root = XML.SubElement(lbl_root, "axisValues")
XML.SubElement(v_root, 'string').text = 'default' XML.SubElement(v_root, "string").text = "default"
elif axis_type == "groovy": elif axis_type == "groovy":
command = XML.SubElement(lbl_root, 'groovyString') command = XML.SubElement(lbl_root, "groovyString")
command.text = axis.get('command') command.text = axis.get("command")
XML.SubElement(lbl_root, 'computedValues').text = '' XML.SubElement(lbl_root, "computedValues").text = ""
elif axis_type == "yaml": elif axis_type == "yaml":
XML.SubElement(v_root, 'string').text = axis.get('filename') XML.SubElement(v_root, "string").text = axis.get("filename")
else: else:
for v in values: for v in values:
XML.SubElement(v_root, 'string').text = str(v) XML.SubElement(v_root, "string").text = str(v)
return root return root

View File

@ -95,93 +95,109 @@ class Maven(jenkins_jobs.modules.base.Base):
sequence = 0 sequence = 0
choices_private_repo = { choices_private_repo = {
'default': "default": "hudson.maven.local_repo.DefaultLocalRepositoryLocator",
'hudson.maven.local_repo.DefaultLocalRepositoryLocator', "local-to-workspace": "hudson.maven.local_repo.PerJobLocalRepositoryLocator",
'local-to-workspace': "local-to-executor": "hudson.maven.local_repo.PerExecutorLocalRepositoryLocator",
'hudson.maven.local_repo.PerJobLocalRepositoryLocator',
'local-to-executor':
'hudson.maven.local_repo.PerExecutorLocalRepositoryLocator',
} }
def root_xml(self, data): def root_xml(self, data):
xml_parent = XML.Element('maven2-moduleset') xml_parent = XML.Element("maven2-moduleset")
if 'maven' not in data: if "maven" not in data:
return xml_parent return xml_parent
# determine version of plugin # determine version of plugin
plugin_info = self.registry.get_plugin_info("Maven Integration plugin") plugin_info = self.registry.get_plugin_info("Maven Integration plugin")
version = pkg_resources.parse_version(plugin_info.get('version', '0')) version = pkg_resources.parse_version(plugin_info.get("version", "0"))
if 'root-module' in data['maven']: if "root-module" in data["maven"]:
root_module = XML.SubElement(xml_parent, 'rootModule') root_module = XML.SubElement(xml_parent, "rootModule")
XML.SubElement(root_module, 'groupId').text = \ XML.SubElement(root_module, "groupId").text = data["maven"]["root-module"][
data['maven']['root-module']['group-id'] "group-id"
XML.SubElement(root_module, 'artifactId').text = \ ]
data['maven']['root-module']['artifact-id'] XML.SubElement(root_module, "artifactId").text = data["maven"][
XML.SubElement(xml_parent, 'goals').text = data['maven']['goals'] "root-module"
]["artifact-id"]
XML.SubElement(xml_parent, "goals").text = data["maven"]["goals"]
maven_opts = data['maven'].get('maven-opts') maven_opts = data["maven"].get("maven-opts")
if maven_opts: if maven_opts:
XML.SubElement(xml_parent, 'mavenOpts').text = maven_opts XML.SubElement(xml_parent, "mavenOpts").text = maven_opts
maven_name = data['maven'].get('maven-name') maven_name = data["maven"].get("maven-name")
if maven_name: if maven_name:
XML.SubElement(xml_parent, 'mavenName').text = maven_name XML.SubElement(xml_parent, "mavenName").text = maven_name
private_repo = data['maven'].get('private-repository') private_repo = data["maven"].get("private-repository")
if private_repo: if private_repo:
if private_repo not in self.choices_private_repo.keys(): if private_repo not in self.choices_private_repo.keys():
raise ValueError('Not a valid private-repository "%s", ' raise ValueError(
'must be one of "%s"' % 'Not a valid private-repository "%s", '
(private_repo, 'must be one of "%s"'
", ".join(self.choices_private_repo.keys()))) % (private_repo, ", ".join(self.choices_private_repo.keys()))
XML.SubElement(xml_parent, )
'localRepository', XML.SubElement(
attrib={'class': xml_parent,
self.choices_private_repo[private_repo]}) "localRepository",
attrib={"class": self.choices_private_repo[private_repo]},
)
XML.SubElement(xml_parent, 'ignoreUpstremChanges').text = str( XML.SubElement(xml_parent, "ignoreUpstremChanges").text = str(
data['maven'].get('ignore-upstream-changes', True)).lower() data["maven"].get("ignore-upstream-changes", True)
).lower()
XML.SubElement(xml_parent, 'rootPOM').text = \ XML.SubElement(xml_parent, "rootPOM").text = data["maven"].get(
data['maven'].get('root-pom', 'pom.xml') "root-pom", "pom.xml"
XML.SubElement(xml_parent, 'aggregatorStyleBuild').text = str( )
not data['maven'].get('parallel-build-modules', False)).lower() XML.SubElement(xml_parent, "aggregatorStyleBuild").text = str(
XML.SubElement(xml_parent, 'incrementalBuild').text = str( not data["maven"].get("parallel-build-modules", False)
data['maven'].get('incremental-build', False)).lower() ).lower()
XML.SubElement(xml_parent, 'siteArchivingDisabled').text = str( XML.SubElement(xml_parent, "incrementalBuild").text = str(
not data['maven'].get('automatic-site-archiving', True)).lower() data["maven"].get("incremental-build", False)
XML.SubElement(xml_parent, 'fingerprintingDisabled').text = str( ).lower()
not data['maven'].get('automatic-fingerprinting', True)).lower() XML.SubElement(xml_parent, "siteArchivingDisabled").text = str(
if (version > pkg_resources.parse_version('0') and not data["maven"].get("automatic-site-archiving", True)
version < pkg_resources.parse_version('2.0.1')): ).lower()
XML.SubElement(xml_parent, 'perModuleEmail').text = str( XML.SubElement(xml_parent, "fingerprintingDisabled").text = str(
data.get('per-module-email', True)).lower() not data["maven"].get("automatic-fingerprinting", True)
XML.SubElement(xml_parent, 'archivingDisabled').text = str( ).lower()
not data['maven'].get('automatic-archiving', True)).lower() if version > pkg_resources.parse_version(
XML.SubElement(xml_parent, 'resolveDependencies').text = str( "0"
data['maven'].get('resolve-dependencies', False)).lower() ) and version < pkg_resources.parse_version("2.0.1"):
XML.SubElement(xml_parent, 'processPlugins').text = str( XML.SubElement(xml_parent, "perModuleEmail").text = str(
data['maven'].get('process-plugins', False)).lower() data.get("per-module-email", True)
XML.SubElement(xml_parent, 'mavenValidationLevel').text = '-1' ).lower()
XML.SubElement(xml_parent, 'runHeadless').text = str( XML.SubElement(xml_parent, "archivingDisabled").text = str(
data['maven'].get('run-headless', False)).lower() not data["maven"].get("automatic-archiving", True)
XML.SubElement(xml_parent, 'disableTriggerDownstreamProjects').text = \ ).lower()
str(data['maven'].get('disable-downstream', False)).lower() XML.SubElement(xml_parent, "resolveDependencies").text = str(
if 'custom-workspace' in data['maven']: data["maven"].get("resolve-dependencies", False)
XML.SubElement(xml_parent, 'customWorkspace').text = str( ).lower()
data['maven'].get('custom-workspace')) XML.SubElement(xml_parent, "processPlugins").text = str(
helpers.config_file_provider_settings(xml_parent, data['maven']) data["maven"].get("process-plugins", False)
).lower()
XML.SubElement(xml_parent, "mavenValidationLevel").text = "-1"
XML.SubElement(xml_parent, "runHeadless").text = str(
data["maven"].get("run-headless", False)
).lower()
XML.SubElement(xml_parent, "disableTriggerDownstreamProjects").text = str(
data["maven"].get("disable-downstream", False)
).lower()
if "custom-workspace" in data["maven"]:
XML.SubElement(xml_parent, "customWorkspace").text = str(
data["maven"].get("custom-workspace")
)
helpers.config_file_provider_settings(xml_parent, data["maven"])
run_post_steps = XML.SubElement(xml_parent, 'runPostStepsIfResult') run_post_steps = XML.SubElement(xml_parent, "runPostStepsIfResult")
run_conditions = ['SUCCESS', 'UNSTABLE', 'FAILURE'] run_conditions = ["SUCCESS", "UNSTABLE", "FAILURE"]
run_condition = data['maven'].get('post-step-run-condition', 'FAILURE') run_condition = data["maven"].get("post-step-run-condition", "FAILURE")
if run_condition not in run_conditions: if run_condition not in run_conditions:
raise InvalidAttributeError('post-step-run-condition', raise InvalidAttributeError(
run_condition, run_conditions) "post-step-run-condition", run_condition, run_conditions
)
cond_dict = hudson_model.THRESHOLDS[run_condition] cond_dict = hudson_model.THRESHOLDS[run_condition]
XML.SubElement(run_post_steps, 'name').text = cond_dict['name'] XML.SubElement(run_post_steps, "name").text = cond_dict["name"]
XML.SubElement(run_post_steps, 'ordinal').text = cond_dict['ordinal'] XML.SubElement(run_post_steps, "ordinal").text = cond_dict["ordinal"]
XML.SubElement(run_post_steps, 'color').text = cond_dict['color'] XML.SubElement(run_post_steps, "color").text = cond_dict["color"]
return xml_parent return xml_parent

File diff suppressed because it is too large Load Diff

View File

@ -59,6 +59,7 @@ class MultiJob(jenkins_jobs.modules.base.Base):
sequence = 0 sequence = 0
def root_xml(self, data): def root_xml(self, data):
xml_parent = XML.Element('com.tikal.jenkins.plugins.multijob.' xml_parent = XML.Element(
'MultiJobProject') "com.tikal.jenkins.plugins.multijob." "MultiJobProject"
)
return xml_parent return xml_parent

View File

@ -82,31 +82,38 @@ import jenkins_jobs.modules.base
class Pipeline(jenkins_jobs.modules.base.Base): class Pipeline(jenkins_jobs.modules.base.Base):
sequence = 0 sequence = 0
error_msg = ("You cannot declare both 'dsl' and 'pipeline-scm' on a " error_msg = "You cannot declare both 'dsl' and 'pipeline-scm' on a " "pipeline job"
"pipeline job")
def root_xml(self, data): def root_xml(self, data):
xml_parent = XML.Element('flow-definition', xml_parent = XML.Element("flow-definition", {"plugin": "workflow-job"})
{'plugin': 'workflow-job'}) if "dsl" in data and "pipeline-scm" in data:
if 'dsl' in data and 'pipeline-scm' in data:
raise JenkinsJobsException(self.error_msg) raise JenkinsJobsException(self.error_msg)
if 'dsl' in data: if "dsl" in data:
xml_definition = XML.SubElement(xml_parent, 'definition', xml_definition = XML.SubElement(
{'plugin': 'workflow-cps', xml_parent,
'class': 'org.jenkinsci.plugins.' "definition",
'workflow.cps.CpsFlowDefinition'}) {
XML.SubElement(xml_definition, 'script').text = data['dsl'] "plugin": "workflow-cps",
elif 'pipeline-scm' in data: "class": "org.jenkinsci.plugins." "workflow.cps.CpsFlowDefinition",
xml_definition = XML.SubElement(xml_parent, 'definition', { },
'plugin': 'workflow-cps', )
'class': 'org.jenkinsci.plugins.workflow.cps.' XML.SubElement(xml_definition, "script").text = data["dsl"]
'CpsScmFlowDefinition'}) elif "pipeline-scm" in data:
xml_definition = XML.SubElement(
xml_parent,
"definition",
{
"plugin": "workflow-cps",
"class": "org.jenkinsci.plugins.workflow.cps."
"CpsScmFlowDefinition",
},
)
else: else:
raise JenkinsJobsException("Either 'dsl' or 'pipeline-scm' " raise JenkinsJobsException(
"is required for pipeline job") "Either 'dsl' or 'pipeline-scm' " "is required for pipeline job"
)
needs_workspace = data.get('sandbox', False) needs_workspace = data.get("sandbox", False)
XML.SubElement(xml_definition, 'sandbox').text = str( XML.SubElement(xml_definition, "sandbox").text = str(needs_workspace).lower()
needs_workspace).lower()
return xml_parent return xml_parent

View File

@ -59,22 +59,21 @@ class Workflow(jenkins_jobs.modules.base.Base):
def root_xml(self, data): def root_xml(self, data):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.warning( logger.warning("Workflow job type is deprecated, please use Pipeline job type")
"Workflow job type is deprecated, please use Pipeline job type"
xml_parent = XML.Element("flow-definition", {"plugin": "workflow-job"})
xml_definition = XML.SubElement(
xml_parent,
"definition",
{
"plugin": "workflow-cps",
"class": "org.jenkinsci.plugins." "workflow.cps.CpsFlowDefinition",
},
) )
xml_parent = XML.Element('flow-definition', mapping = [("dsl", "script", None), ("sandbox", "sandbox", False)]
{'plugin': 'workflow-job'})
xml_definition = XML.SubElement(xml_parent, 'definition',
{'plugin': 'workflow-cps',
'class': 'org.jenkinsci.plugins.'
'workflow.cps.CpsFlowDefinition'})
mapping = [
('dsl', 'script', None),
('sandbox', 'sandbox', False),
]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(
xml_definition, data, mapping, fail_required=True) xml_definition, data, mapping, fail_required=True
)
return xml_parent return xml_parent

View File

@ -59,16 +59,17 @@ def builds_chain_fingerprinter(registry, xml_parent, data):
.. literalinclude:: /../../tests/properties/fixtures/fingerprinter.yaml .. literalinclude:: /../../tests/properties/fixtures/fingerprinter.yaml
:language: yaml :language: yaml
""" """
fingerprinter = XML.SubElement(xml_parent, fingerprinter = XML.SubElement(
'org.jenkinsci.plugins.' xml_parent,
'buildschainfingerprinter.' "org.jenkinsci.plugins."
'AutomaticFingerprintJobProperty') "buildschainfingerprinter."
"AutomaticFingerprintJobProperty",
)
mapping = [ mapping = [
('per-builds-chain', 'isPerBuildsChainEnabled', False), ("per-builds-chain", "isPerBuildsChainEnabled", False),
('per-job-chain', 'isPerJobsChainEnabled', False), ("per-job-chain", "isPerJobsChainEnabled", False),
] ]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(fingerprinter, data, mapping, fail_required=True)
fingerprinter, data, mapping, fail_required=True)
def ownership(registry, xml_parent, data): def ownership(registry, xml_parent, data):
@ -87,16 +88,17 @@ def ownership(registry, xml_parent, data):
""" """
ownership_plugin = XML.SubElement( ownership_plugin = XML.SubElement(
xml_parent, xml_parent,
'com.synopsys.arc.jenkins.plugins.ownership.jobs.JobOwnerJobProperty') "com.synopsys.arc.jenkins.plugins.ownership.jobs.JobOwnerJobProperty",
ownership = XML.SubElement(ownership_plugin, 'ownership') )
owner = str(data.get('enabled', True)).lower() ownership = XML.SubElement(ownership_plugin, "ownership")
XML.SubElement(ownership, 'ownershipEnabled').text = owner owner = str(data.get("enabled", True)).lower()
XML.SubElement(ownership, "ownershipEnabled").text = owner
XML.SubElement(ownership, 'primaryOwnerId').text = data.get('owner') XML.SubElement(ownership, "primaryOwnerId").text = data.get("owner")
coownersIds = XML.SubElement(ownership, 'coownersIds') coownersIds = XML.SubElement(ownership, "coownersIds")
for coowner in data.get('co-owners', []): for coowner in data.get("co-owners", []):
XML.SubElement(coownersIds, 'string').text = coowner XML.SubElement(coownersIds, "string").text = coowner
def promoted_build(registry, xml_parent, data): def promoted_build(registry, xml_parent, data):
@ -115,13 +117,14 @@ def promoted_build(registry, xml_parent, data):
.. literalinclude:: /../../tests/properties/fixtures/promoted_build.yaml .. literalinclude:: /../../tests/properties/fixtures/promoted_build.yaml
:language: yaml :language: yaml
""" """
promoted = XML.SubElement(xml_parent, 'hudson.plugins.promoted__builds.' promoted = XML.SubElement(
'JobPropertyImpl') xml_parent, "hudson.plugins.promoted__builds." "JobPropertyImpl"
names = data.get('names', []) )
names = data.get("names", [])
if names: if names:
active_processes = XML.SubElement(promoted, 'activeProcessNames') active_processes = XML.SubElement(promoted, "activeProcessNames")
for n in names: for n in names:
XML.SubElement(active_processes, 'string').text = str(n) XML.SubElement(active_processes, "string").text = str(n)
def gitbucket(parser, xml_parent, data): def gitbucket(parser, xml_parent, data):
@ -143,15 +146,12 @@ def gitbucket(parser, xml_parent, data):
:language: yaml :language: yaml
""" """
gitbucket = XML.SubElement( gitbucket = XML.SubElement(
xml_parent, 'org.jenkinsci.plugins.gitbucket.GitBucketProjectProperty') xml_parent, "org.jenkinsci.plugins.gitbucket.GitBucketProjectProperty"
gitbucket.set('plugin', 'gitbucket') )
gitbucket.set("plugin", "gitbucket")
mapping = [ mapping = [("url", "url", None), ("link-enabled", "linkEnabled", False)]
('url', 'url', None), helpers.convert_mapping_to_xml(gitbucket, data, mapping, fail_required=True)
('link-enabled', 'linkEnabled', False),
]
helpers.convert_mapping_to_xml(
gitbucket, data, mapping, fail_required=True)
def github(registry, xml_parent, data): def github(registry, xml_parent, data):
@ -174,13 +174,11 @@ def github(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
github = XML.SubElement( github = XML.SubElement(
xml_parent, 'com.coravy.hudson.plugins.github.GithubProjectProperty') xml_parent, "com.coravy.hudson.plugins.github.GithubProjectProperty"
github.set('plugin', 'github') )
github.set("plugin", "github")
mapping = [ mapping = [("url", "projectUrl", None), ("display-name", "displayName", "")]
('url', 'projectUrl', None),
('display-name', 'displayName', ''),
]
helpers.convert_mapping_to_xml(github, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(github, data, mapping, fail_required=True)
@ -197,12 +195,11 @@ def gitlab(registry, xml_parent, data):
.. literalinclude:: /../../tests/properties/fixtures/gitlab.yaml .. literalinclude:: /../../tests/properties/fixtures/gitlab.yaml
:language: yaml :language: yaml
""" """
gitlab = XML.SubElement(xml_parent, gitlab = XML.SubElement(
'com.dabsquared.gitlabjenkins.connection.' xml_parent,
'GitLabConnectionProperty') "com.dabsquared.gitlabjenkins.connection." "GitLabConnectionProperty",
mapping = [ )
('connection', 'gitLabConnection', None), mapping = [("connection", "gitLabConnection", None)]
]
helpers.convert_mapping_to_xml(gitlab, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(gitlab, data, mapping, fail_required=True)
@ -219,12 +216,10 @@ def gitlab_logo(registry, xml_parent, data):
.. literalinclude:: /../../tests/properties/fixtures/gitlab-logo.yaml .. literalinclude:: /../../tests/properties/fixtures/gitlab-logo.yaml
:language: yaml :language: yaml
""" """
logo = XML.SubElement(xml_parent, logo = XML.SubElement(
'org.jenkinsci.plugins.gitlablogo.' xml_parent, "org.jenkinsci.plugins.gitlablogo." "GitlabLogoProperty"
'GitlabLogoProperty') )
mapping = [ mapping = [("repository-name", "repositoryName", None)]
('repository-name', 'repositoryName', None)
]
helpers.convert_mapping_to_xml(logo, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(logo, data, mapping, fail_required=True)
@ -238,9 +233,7 @@ def disk_usage(registry, xml_parent, data):
.. literalinclude:: /../../tests/properties/fixtures/disk-usage.yaml .. literalinclude:: /../../tests/properties/fixtures/disk-usage.yaml
:language: yaml :language: yaml
""" """
XML.SubElement(xml_parent, XML.SubElement(xml_parent, "hudson.plugins.disk__usage." "DiskUsageProperty")
'hudson.plugins.disk__usage.'
'DiskUsageProperty')
def least_load(registry, xml_parent, data): def least_load(registry, xml_parent, data):
@ -255,12 +248,11 @@ def least_load(registry, xml_parent, data):
.. literalinclude:: /../../tests/properties/fixtures/least-load002.yaml .. literalinclude:: /../../tests/properties/fixtures/least-load002.yaml
:language: yaml :language: yaml
""" """
least = XML.SubElement(xml_parent, least = XML.SubElement(
'org.bstick12.jenkinsci.plugins.leastload.' xml_parent,
'LeastLoadDisabledProperty') "org.bstick12.jenkinsci.plugins.leastload." "LeastLoadDisabledProperty",
mapping = [ )
('disabled', 'leastLoadDisabled', True), mapping = [("disabled", "leastLoadDisabled", True)]
]
helpers.convert_mapping_to_xml(least, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(least, data, mapping, fail_required=True)
@ -288,43 +280,41 @@ def throttle(registry, xml_parent, data):
.. literalinclude:: /../../tests/properties/fixtures/throttle001.yaml .. literalinclude:: /../../tests/properties/fixtures/throttle001.yaml
:language: yaml :language: yaml
""" """
throttle = XML.SubElement(xml_parent, throttle = XML.SubElement(
'hudson.plugins.throttleconcurrents.' xml_parent, "hudson.plugins.throttleconcurrents." "ThrottleJobProperty"
'ThrottleJobProperty') )
mapping = [ mapping = [
('max-per-node', 'maxConcurrentPerNode', '0'), ("max-per-node", "maxConcurrentPerNode", "0"),
('max-total', 'maxConcurrentTotal', '0'), ("max-total", "maxConcurrentTotal", "0"),
('enabled', 'throttleEnabled', True), ("enabled", "throttleEnabled", True),
] ]
helpers.convert_mapping_to_xml(throttle, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(throttle, data, mapping, fail_required=True)
cat = data.get('categories', []) cat = data.get("categories", [])
if cat: if cat:
cn = XML.SubElement(throttle, 'categories') cn = XML.SubElement(throttle, "categories")
for c in cat: for c in cat:
XML.SubElement(cn, 'string').text = str(c) XML.SubElement(cn, "string").text = str(c)
options_list = ('category', 'project') options_list = ("category", "project")
option = data.get('option') option = data.get("option")
if option not in options_list: if option not in options_list:
raise InvalidAttributeError('option', option, options_list) raise InvalidAttributeError("option", option, options_list)
mapping = [ mapping = [
('', 'throttleOption', option), ("", "throttleOption", option),
('', 'configVersion', '1'), ("", "configVersion", "1"),
('parameters-limit', 'limitOneJobWithMatchingParams', False), ("parameters-limit", "limitOneJobWithMatchingParams", False),
] ]
helpers.convert_mapping_to_xml(throttle, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(throttle, data, mapping, fail_required=True)
matrixopt = XML.SubElement(throttle, 'matrixOptions') matrixopt = XML.SubElement(throttle, "matrixOptions")
mapping = [ mapping = [
('matrix-builds', 'throttleMatrixBuilds', True), ("matrix-builds", "throttleMatrixBuilds", True),
('matrix-configs', 'throttleMatrixConfigurations', False), ("matrix-configs", "throttleMatrixConfigurations", False),
] ]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(matrixopt, data, mapping, fail_required=True)
matrixopt, data, mapping, fail_required=True)
params_to_use = data.get('parameters-check-list', []) params_to_use = data.get("parameters-check-list", [])
XML.SubElement(throttle, 'paramsToUseForLimit').text = ",".join( XML.SubElement(throttle, "paramsToUseForLimit").text = ",".join(params_to_use)
params_to_use)
def branch_api(registry, xml_parent, data): def branch_api(registry, xml_parent, data):
@ -354,16 +344,17 @@ def branch_api(registry, xml_parent, data):
/../../tests/properties/fixtures/branch-api-full.yaml /../../tests/properties/fixtures/branch-api-full.yaml
:language: yaml :language: yaml
""" """
branch = XML.SubElement(xml_parent, 'jenkins.branch.' branch = XML.SubElement(
'RateLimitBranchProperty_-JobPropertyImpl') xml_parent, "jenkins.branch." "RateLimitBranchProperty_-JobPropertyImpl"
branch.set('plugin', 'branch-api') )
branch.set("plugin", "branch-api")
valid_time_periods = ['Hour', 'Day', 'Week', 'Month', 'Year'] valid_time_periods = ["Hour", "Day", "Week", "Month", "Year"]
mapping = [ mapping = [
('time-period', 'durationName', 'Hour', valid_time_periods), ("time-period", "durationName", "Hour", valid_time_periods),
('number-of-builds', 'count', 1), ("number-of-builds", "count", 1),
('skip-rate-limit', 'userBoost', False), ("skip-rate-limit", "userBoost", False),
] ]
helpers.convert_mapping_to_xml(branch, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(branch, data, mapping, fail_required=True)
@ -383,19 +374,16 @@ def sidebar(registry, xml_parent, data):
.. literalinclude:: /../../tests/properties/fixtures/sidebar02.yaml .. literalinclude:: /../../tests/properties/fixtures/sidebar02.yaml
:language: yaml :language: yaml
""" """
sidebar = xml_parent.find('hudson.plugins.sidebar__link.ProjectLinks') sidebar = xml_parent.find("hudson.plugins.sidebar__link.ProjectLinks")
if sidebar is None: if sidebar is None:
sidebar = XML.SubElement(xml_parent, sidebar = XML.SubElement(
'hudson.plugins.sidebar__link.ProjectLinks') xml_parent, "hudson.plugins.sidebar__link.ProjectLinks"
links = XML.SubElement(sidebar, 'links') )
links = XML.SubElement(sidebar, "links")
else: else:
links = sidebar.find('links') links = sidebar.find("links")
action = XML.SubElement(links, 'hudson.plugins.sidebar__link.LinkAction') action = XML.SubElement(links, "hudson.plugins.sidebar__link.LinkAction")
mapping = [ mapping = [("url", "url", ""), ("text", "text", ""), ("icon", "icon", "")]
('url', 'url', ''),
('text', 'text', ''),
('icon', 'icon', ''),
]
helpers.convert_mapping_to_xml(action, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(action, data, mapping, fail_required=True)
@ -423,43 +411,40 @@ def inject(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
inject = XML.SubElement(xml_parent, inject = XML.SubElement(xml_parent, "EnvInjectJobProperty")
'EnvInjectJobProperty') info = XML.SubElement(inject, "info")
info = XML.SubElement(inject, 'info')
mapping = [ mapping = [
('properties-file', 'propertiesFilePath', None), ("properties-file", "propertiesFilePath", None),
('properties-content', 'propertiesContent', None), ("properties-content", "propertiesContent", None),
('script-file', 'scriptFilePath', None), ("script-file", "scriptFilePath", None),
('script-content', 'scriptContent', None), ("script-content", "scriptContent", None),
('load-from-master', 'loadFilesFromMaster', False), ("load-from-master", "loadFilesFromMaster", False),
] ]
helpers.convert_mapping_to_xml(info, data, mapping, fail_required=False) helpers.convert_mapping_to_xml(info, data, mapping, fail_required=False)
# determine version of plugin # determine version of plugin
plugin_info = registry.get_plugin_info("Groovy") plugin_info = registry.get_plugin_info("Groovy")
version = pkg_resources.parse_version(plugin_info.get('version', '0')) version = pkg_resources.parse_version(plugin_info.get("version", "0"))
if version >= pkg_resources.parse_version("2.0.0"): if version >= pkg_resources.parse_version("2.0.0"):
secure_groovy_script = XML.SubElement(info, 'secureGroovyScript') secure_groovy_script = XML.SubElement(info, "secureGroovyScript")
mapping = [ mapping = [
('groovy-content', 'script', None), ("groovy-content", "script", None),
('groovy-sandbox', 'sandbox', False), ("groovy-sandbox", "sandbox", False),
] ]
helpers.convert_mapping_to_xml(secure_groovy_script, data, mapping, helpers.convert_mapping_to_xml(
fail_required=False) secure_groovy_script, data, mapping, fail_required=False
)
else: else:
mapping = [ mapping = [("groovy-content", "groovyScriptContent", None)]
('groovy-content', 'groovyScriptContent', None), helpers.convert_mapping_to_xml(info, data, mapping, fail_required=False)
]
helpers.convert_mapping_to_xml(info, data, mapping,
fail_required=False)
mapping = [ mapping = [
('enabled', 'on', True), ("enabled", "on", True),
('keep-system-variables', 'keepJenkinsSystemVariables', True), ("keep-system-variables", "keepJenkinsSystemVariables", True),
('keep-build-variables', 'keepBuildVariables', True), ("keep-build-variables", "keepBuildVariables", True),
('override-build-parameters', 'overrideBuildParameters', False), ("override-build-parameters", "overrideBuildParameters", False),
] ]
helpers.convert_mapping_to_xml(inject, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(inject, data, mapping, fail_required=True)
@ -479,11 +464,12 @@ def authenticated_build(registry, xml_parent, data):
""" """
# TODO: generalize this # TODO: generalize this
security = XML.SubElement(xml_parent, security = XML.SubElement(
'hudson.security.' xml_parent, "hudson.security." "AuthorizationMatrixProperty"
'AuthorizationMatrixProperty') )
XML.SubElement(security, 'permission').text = ( XML.SubElement(
'hudson.model.Item.Build:authenticated') security, "permission"
).text = "hudson.model.Item.Build:authenticated"
def authorization(registry, xml_parent, data): def authorization(registry, xml_parent, data):
@ -523,38 +509,39 @@ def authorization(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
credentials = 'com.cloudbees.plugins.credentials.CredentialsProvider.' credentials = "com.cloudbees.plugins.credentials.CredentialsProvider."
ownership = 'com.synopsys.arc.jenkins.plugins.ownership.OwnershipPlugin.' ownership = "com.synopsys.arc.jenkins.plugins.ownership.OwnershipPlugin."
mapping = { mapping = {
'credentials-create': ''.join((credentials, 'Create')), "credentials-create": "".join((credentials, "Create")),
'credentials-delete': ''.join((credentials, 'Delete')), "credentials-delete": "".join((credentials, "Delete")),
'credentials-manage-domains': ''.join((credentials, 'ManageDomains')), "credentials-manage-domains": "".join((credentials, "ManageDomains")),
'credentials-update': ''.join((credentials, 'Update')), "credentials-update": "".join((credentials, "Update")),
'credentials-view': ''.join((credentials, 'View')), "credentials-view": "".join((credentials, "View")),
'job-build': 'hudson.model.Item.Build', "job-build": "hudson.model.Item.Build",
'job-cancel': 'hudson.model.Item.Cancel', "job-cancel": "hudson.model.Item.Cancel",
'job-configure': 'hudson.model.Item.Configure', "job-configure": "hudson.model.Item.Configure",
'job-delete': 'hudson.model.Item.Delete', "job-delete": "hudson.model.Item.Delete",
'job-discover': 'hudson.model.Item.Discover', "job-discover": "hudson.model.Item.Discover",
'job-extended-read': 'hudson.model.Item.ExtendedRead', "job-extended-read": "hudson.model.Item.ExtendedRead",
'job-move': 'hudson.model.Item.Move', "job-move": "hudson.model.Item.Move",
'job-read': 'hudson.model.Item.Read', "job-read": "hudson.model.Item.Read",
'job-status': 'hudson.model.Item.ViewStatus', "job-status": "hudson.model.Item.ViewStatus",
'job-workspace': 'hudson.model.Item.Workspace', "job-workspace": "hudson.model.Item.Workspace",
'ownership-jobs': ''.join((ownership, 'Jobs')), "ownership-jobs": "".join((ownership, "Jobs")),
'run-delete': 'hudson.model.Run.Delete', "run-delete": "hudson.model.Run.Delete",
'run-replay': 'hudson.model.Run.Replay', "run-replay": "hudson.model.Run.Replay",
'run-update': 'hudson.model.Run.Update', "run-update": "hudson.model.Run.Update",
'scm-tag': 'hudson.scm.SCM.Tag', "scm-tag": "hudson.scm.SCM.Tag",
} }
if data: if data:
matrix = XML.SubElement(xml_parent, matrix = XML.SubElement(
'hudson.security.AuthorizationMatrixProperty') xml_parent, "hudson.security.AuthorizationMatrixProperty"
)
for (username, perms) in data.items(): for (username, perms) in data.items():
for perm in perms: for perm in perms:
pe = XML.SubElement(matrix, 'permission') pe = XML.SubElement(matrix, "permission")
try: try:
pe.text = "{0}:{1}".format(mapping[perm], username) pe.text = "{0}:{1}".format(mapping[perm], username)
except KeyError: except KeyError:
@ -577,39 +564,32 @@ def priority_sorter(registry, xml_parent, data):
/../../tests/properties/fixtures/priority_sorter002.yaml /../../tests/properties/fixtures/priority_sorter002.yaml
:language: yaml :language: yaml
""" """
plugin_info = registry.get_plugin_info('PrioritySorter') plugin_info = registry.get_plugin_info("PrioritySorter")
version = pkg_resources.parse_version(plugin_info.get('version', '0')) version = pkg_resources.parse_version(plugin_info.get("version", "0"))
if version >= pkg_resources.parse_version("3.0"): if version >= pkg_resources.parse_version("3.0"):
priority_sorter_tag = XML.SubElement( priority_sorter_tag = XML.SubElement(
xml_parent, xml_parent,
'jenkins.advancedqueue.jobinclusion.' "jenkins.advancedqueue.jobinclusion." "strategy.JobInclusionJobProperty",
'strategy.JobInclusionJobProperty') )
mapping = [ mapping = [("use", "useJobGroup", True), ("priority", "jobGroupName", None)]
('use', 'useJobGroup', True),
('priority', 'jobGroupName', None)
]
elif version >= pkg_resources.parse_version("2.0"): elif version >= pkg_resources.parse_version("2.0"):
priority_sorter_tag = XML.SubElement(xml_parent, priority_sorter_tag = XML.SubElement(
'jenkins.advancedqueue.priority.' xml_parent, "jenkins.advancedqueue.priority." "strategy.PriorityJobProperty"
'strategy.PriorityJobProperty') )
mapping = [ mapping = [("use", "useJobPriority", True), ("priority", "priority", None)]
('use', 'useJobPriority', True),
('priority', 'priority', None)
]
else: else:
priority_sorter_tag = XML.SubElement(xml_parent, priority_sorter_tag = XML.SubElement(
'hudson.queueSorter.' xml_parent, "hudson.queueSorter." "PrioritySorterJobProperty"
'PrioritySorterJobProperty') )
mapping = [ mapping = [("priority", "priority", None)]
('priority', 'priority', None),
]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(
priority_sorter_tag, data, mapping, fail_required=True) priority_sorter_tag, data, mapping, fail_required=True
)
def build_blocker(registry, xml_parent, data): def build_blocker(registry, xml_parent, data):
@ -643,25 +623,25 @@ def build_blocker(registry, xml_parent, data):
/../../tests/properties/fixtures/build-blocker-full.yaml /../../tests/properties/fixtures/build-blocker-full.yaml
:language: yaml :language: yaml
""" """
blocker = XML.SubElement(xml_parent, blocker = XML.SubElement(
'hudson.plugins.' xml_parent, "hudson.plugins." "buildblocker.BuildBlockerProperty"
'buildblocker.BuildBlockerProperty') )
if data is None or 'blocking-jobs' not in data: if data is None or "blocking-jobs" not in data:
raise JenkinsJobsException('blocking-jobs field is missing') raise JenkinsJobsException("blocking-jobs field is missing")
elif data.get('blocking-jobs', None) is None: elif data.get("blocking-jobs", None) is None:
raise JenkinsJobsException('blocking-jobs list must not be empty') raise JenkinsJobsException("blocking-jobs list must not be empty")
jobs = '' jobs = ""
for setting, value in data.items(): for setting, value in data.items():
if setting == 'blocking-jobs': if setting == "blocking-jobs":
jobs = '\n'.join(value) jobs = "\n".join(value)
block_level_types = ['GLOBAL', 'NODE'] block_level_types = ["GLOBAL", "NODE"]
queue_scan_types = ['DISABLED', 'ALL', 'BUILDABLE'] queue_scan_types = ["DISABLED", "ALL", "BUILDABLE"]
mapping = [ mapping = [
('use-build-blocker', 'useBuildBlocker', True), ("use-build-blocker", "useBuildBlocker", True),
('', 'blockingJobs', jobs), ("", "blockingJobs", jobs),
('block-level', 'blockLevel', 'GLOBAL', block_level_types), ("block-level", "blockLevel", "GLOBAL", block_level_types),
('queue-scanning', 'scanQueueFor', 'DISABLED', queue_scan_types), ("queue-scanning", "scanQueueFor", "DISABLED", queue_scan_types),
] ]
helpers.convert_mapping_to_xml(blocker, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(blocker, data, mapping, fail_required=True)
@ -684,17 +664,16 @@ def copyartifact(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
copyartifact = XML.SubElement(xml_parent, copyartifact = XML.SubElement(
'hudson.plugins.' xml_parent,
'copyartifact.' "hudson.plugins." "copyartifact." "CopyArtifactPermissionProperty",
'CopyArtifactPermissionProperty', plugin="copyartifact",
plugin='copyartifact') )
if not data or not data.get('projects', None): if not data or not data.get("projects", None):
raise JenkinsJobsException("projects string must exist and " raise JenkinsJobsException("projects string must exist and " "not be empty")
"not be empty") projectlist = XML.SubElement(copyartifact, "projectNameList")
projectlist = XML.SubElement(copyartifact, 'projectNameList') for project in str(data.get("projects")).split(","):
for project in str(data.get('projects')).split(','): XML.SubElement(projectlist, "string").text = project
XML.SubElement(projectlist, 'string').text = project
def batch_tasks(registry, xml_parent, data): def batch_tasks(registry, xml_parent, data):
@ -722,18 +701,12 @@ def batch_tasks(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
pdef = XML.SubElement(xml_parent, pdef = XML.SubElement(xml_parent, "hudson.plugins.batch__task.BatchTaskProperty")
'hudson.plugins.batch__task.BatchTaskProperty') tasks = XML.SubElement(pdef, "tasks")
tasks = XML.SubElement(pdef, 'tasks')
for task in data: for task in data:
batch_task = XML.SubElement(tasks, batch_task = XML.SubElement(tasks, "hudson.plugins.batch__task.BatchTask")
'hudson.plugins.batch__task.BatchTask') mapping = [("name", "name", None), ("script", "script", None)]
mapping = [ helpers.convert_mapping_to_xml(batch_task, task, mapping, fail_required=True)
('name', 'name', None),
('script', 'script', None),
]
helpers.convert_mapping_to_xml(
batch_task, task, mapping, fail_required=True)
def heavy_job(registry, xml_parent, data): def heavy_job(registry, xml_parent, data):
@ -752,12 +725,10 @@ def heavy_job(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
heavyjob = XML.SubElement(xml_parent, heavyjob = XML.SubElement(
'hudson.plugins.' xml_parent, "hudson.plugins." "heavy__job.HeavyJobProperty"
'heavy__job.HeavyJobProperty') )
mapping = [ mapping = [("weight", "weight", 1)]
('weight', 'weight', 1),
]
helpers.convert_mapping_to_xml(heavyjob, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(heavyjob, data, mapping, fail_required=True)
@ -782,18 +753,18 @@ def slave_utilization(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
utilization = XML.SubElement( utilization = XML.SubElement(
xml_parent, 'com.suryagaddipati.jenkins.SlaveUtilizationProperty') xml_parent, "com.suryagaddipati.jenkins.SlaveUtilizationProperty"
)
percent = int(data.get('slave-percentage', 0)) percent = int(data.get("slave-percentage", 0))
exclusive_node_access = True if percent else False exclusive_node_access = True if percent else False
mapping = [ mapping = [
('', 'needsExclusiveAccessToNode', exclusive_node_access), ("", "needsExclusiveAccessToNode", exclusive_node_access),
('', 'slaveUtilizationPercentage', percent), ("", "slaveUtilizationPercentage", percent),
('single-instance-per-slave', 'singleInstancePerSlave', False), ("single-instance-per-slave", "singleInstancePerSlave", False),
] ]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(utilization, data, mapping, fail_required=True)
utilization, data, mapping, fail_required=True)
def delivery_pipeline(registry, xml_parent, data): def delivery_pipeline(registry, xml_parent, data):
@ -818,14 +789,13 @@ def delivery_pipeline(registry, xml_parent, data):
/../../tests/properties/fixtures/delivery-pipeline-full.yaml /../../tests/properties/fixtures/delivery-pipeline-full.yaml
:language: yaml :language: yaml
""" """
pipeline = XML.SubElement( pipeline = XML.SubElement(xml_parent, "se.diabol.jenkins.pipeline.PipelineProperty")
xml_parent, 'se.diabol.jenkins.pipeline.PipelineProperty') pipeline.set("plugin", "delivery-pipeline-plugin")
pipeline.set('plugin', 'delivery-pipeline-plugin')
mapping = [ mapping = [
('stage', 'stageName', ''), ("stage", "stageName", ""),
('task', 'taskName', ''), ("task", "taskName", ""),
('description', 'descriptionTemplate', ''), ("description", "descriptionTemplate", ""),
] ]
helpers.convert_mapping_to_xml(pipeline, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(pipeline, data, mapping, fail_required=True)
@ -846,14 +816,12 @@ def zeromq_event(registry, xml_parent, data):
""" """
zmq_event = XML.SubElement(xml_parent, zmq_event = XML.SubElement(
'org.jenkinsci.plugins.' xml_parent,
'ZMQEventPublisher.HudsonNotificationProperty') "org.jenkinsci.plugins." "ZMQEventPublisher.HudsonNotificationProperty",
mapping = [ )
('', 'enabled', True), mapping = [("", "enabled", True)]
] helpers.convert_mapping_to_xml(zmq_event, data, mapping, fail_required=True)
helpers.convert_mapping_to_xml(
zmq_event, data, mapping, fail_required=True)
def slack(registry, xml_parent, data): def slack(registry, xml_parent, data):
@ -896,39 +864,36 @@ def slack(registry, xml_parent, data):
""" """
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
plugin_info = registry.get_plugin_info('Slack Notification Plugin') plugin_info = registry.get_plugin_info("Slack Notification Plugin")
plugin_ver = pkg_resources.parse_version(plugin_info.get('version', "0")) plugin_ver = pkg_resources.parse_version(plugin_info.get("version", "0"))
if plugin_ver >= pkg_resources.parse_version("2.0"): if plugin_ver >= pkg_resources.parse_version("2.0"):
logger.warning( logger.warning("properties section is not used with plugin version >= 2.0")
"properties section is not used with plugin version >= 2.0",
)
mapping = ( mapping = (
('notify-start', 'startNotification', False), ("notify-start", "startNotification", False),
('notify-success', 'notifySuccess', False), ("notify-success", "notifySuccess", False),
('notify-aborted', 'notifyAborted', False), ("notify-aborted", "notifyAborted", False),
('notify-not-built', 'notifyNotBuilt', False), ("notify-not-built", "notifyNotBuilt", False),
('notify-unstable', 'notifyUnstable', False), ("notify-unstable", "notifyUnstable", False),
('notify-failure', 'notifyFailure', False), ("notify-failure", "notifyFailure", False),
('notify-back-to-normal', 'notifyBackToNormal', False), ("notify-back-to-normal", "notifyBackToNormal", False),
('notify-repeated-failure', 'notifyRepeatedFailure', False), ("notify-repeated-failure", "notifyRepeatedFailure", False),
('include-test-summary', 'includeTestSummary', False), ("include-test-summary", "includeTestSummary", False),
('include-custom-message', 'includeCustomMessage', False), ("include-custom-message", "includeCustomMessage", False),
('custom-message', 'customMessage', ''), ("custom-message", "customMessage", ""),
('room', 'room', ''), ("room", "room", ""),
) )
slack = XML.SubElement( slack = XML.SubElement(
xml_parent, xml_parent, "jenkins.plugins.slack.SlackNotifier_-SlackJobProperty"
'jenkins.plugins.slack.SlackNotifier_-SlackJobProperty',
) )
# Ensure that custom-message is set when include-custom-message is set # Ensure that custom-message is set when include-custom-message is set
# to true. # to true.
if data.get('include-custom-message', False): if data.get("include-custom-message", False):
if not data.get('custom-message', ''): if not data.get("custom-message", ""):
raise MissingAttributeError('custom-message') raise MissingAttributeError("custom-message")
helpers.convert_mapping_to_xml(slack, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(slack, data, mapping, fail_required=True)
@ -955,16 +920,14 @@ def rebuild(registry, xml_parent, data):
.. literalinclude:: /../../tests/properties/fixtures/rebuild-full.yaml .. literalinclude:: /../../tests/properties/fixtures/rebuild-full.yaml
:language: yaml :language: yaml
""" """
sub_element = XML.SubElement(xml_parent, sub_element = XML.SubElement(xml_parent, "com.sonyericsson.rebuild.RebuildSettings")
'com.sonyericsson.rebuild.RebuildSettings') sub_element.set("plugin", "rebuild")
sub_element.set('plugin', 'rebuild')
mapping = [ mapping = [
('auto-rebuild', 'autoRebuild', False), ("auto-rebuild", "autoRebuild", False),
('rebuild-disabled', 'rebuildDisabled', False), ("rebuild-disabled", "rebuildDisabled", False),
] ]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(sub_element, data, mapping, fail_required=True)
sub_element, data, mapping, fail_required=True)
def build_discarder(registry, xml_parent, data): def build_discarder(registry, xml_parent, data):
@ -987,19 +950,17 @@ def build_discarder(registry, xml_parent, data):
/../../tests/properties/fixtures/build-discarder-002.yaml /../../tests/properties/fixtures/build-discarder-002.yaml
:language: yaml :language: yaml
""" """
base_sub = XML.SubElement(xml_parent, base_sub = XML.SubElement(xml_parent, "jenkins.model.BuildDiscarderProperty")
'jenkins.model.BuildDiscarderProperty') strategy = XML.SubElement(base_sub, "strategy")
strategy = XML.SubElement(base_sub, 'strategy') strategy.set("class", "hudson.tasks.LogRotator")
strategy.set('class', 'hudson.tasks.LogRotator')
mappings = [ mappings = [
('days-to-keep', 'daysToKeep', -1), ("days-to-keep", "daysToKeep", -1),
('num-to-keep', 'numToKeep', -1), ("num-to-keep", "numToKeep", -1),
('artifact-days-to-keep', 'artifactDaysToKeep', -1), ("artifact-days-to-keep", "artifactDaysToKeep", -1),
('artifact-num-to-keep', 'artifactNumToKeep', -1), ("artifact-num-to-keep", "artifactNumToKeep", -1),
] ]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(strategy, data, mappings, fail_required=True)
strategy, data, mappings, fail_required=True)
def slave_prerequisites(registry, xml_parent, data): def slave_prerequisites(registry, xml_parent, data):
@ -1027,14 +988,16 @@ def slave_prerequisites(registry, xml_parent, data):
/../../tests/properties/fixtures/slave-prerequisites-full.yaml /../../tests/properties/fixtures/slave-prerequisites-full.yaml
:language: yaml :language: yaml
""" """
prereqs = XML.SubElement(xml_parent, prereqs = XML.SubElement(xml_parent, "com.cloudbees.plugins.JobPrerequisites")
'com.cloudbees.plugins.JobPrerequisites')
mappings = [ mappings = [
('script', 'script', None), ("script", "script", None),
('interpreter', 'interpreter', 'shell', { (
'cmd': 'windows batch command', "interpreter",
'shell': 'shell script'}), "interpreter",
"shell",
{"cmd": "windows batch command", "shell": "shell script"},
),
] ]
helpers.convert_mapping_to_xml(prereqs, data, mappings, fail_required=True) helpers.convert_mapping_to_xml(prereqs, data, mappings, fail_required=True)
@ -1077,24 +1040,23 @@ def groovy_label(registry, xml_parent, data):
/../../tests/properties/fixtures/groovy-label-full.yaml /../../tests/properties/fixtures/groovy-label-full.yaml
:language: yaml :language: yaml
""" """
sub_element = XML.SubElement(xml_parent, sub_element = XML.SubElement(
'jp.ikedam.jenkins.plugins.' xml_parent,
'groovy__label__assignment.' "jp.ikedam.jenkins.plugins."
'GroovyLabelAssignmentProperty') "groovy__label__assignment."
sub_element.set('plugin', 'groovy-label-assignment') "GroovyLabelAssignmentProperty",
security = XML.SubElement(sub_element, 'secureGroovyScript') )
security.set('plugin', 'script-security') sub_element.set("plugin", "groovy-label-assignment")
mapping = [ security = XML.SubElement(sub_element, "secureGroovyScript")
('script', 'script', ''), security.set("plugin", "script-security")
('sandbox', 'sandbox', False), mapping = [("script", "script", ""), ("sandbox", "sandbox", False)]
]
helpers.convert_mapping_to_xml(security, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(security, data, mapping, fail_required=True)
if data and 'classpath' in data: if data and "classpath" in data:
classpath = XML.SubElement(security, 'classpath') classpath = XML.SubElement(security, "classpath")
for value in data['classpath']: for value in data["classpath"]:
entry = XML.SubElement(classpath, 'entry') entry = XML.SubElement(classpath, "entry")
XML.SubElement(entry, 'url').text = value XML.SubElement(entry, "url").text = value
def lockable_resources(registry, xml_parent, data): def lockable_resources(registry, xml_parent, data):
@ -1137,26 +1099,24 @@ def lockable_resources(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
lockable_resources = XML.SubElement( lockable_resources = XML.SubElement(
xml_parent, xml_parent, "org.jenkins.plugins.lockableresources.RequiredResourcesProperty"
'org.jenkins.plugins.lockableresources.RequiredResourcesProperty') )
if data.get('resources') and data.get('label'): if data.get("resources") and data.get("label"):
raise AttributeConflictError('resources', ('label',)) raise AttributeConflictError("resources", ("label",))
mapping = [ mapping = [
('resources', 'resourceNames', ''), ("resources", "resourceNames", ""),
('var-name', 'resourceNamesVar', ''), ("var-name", "resourceNamesVar", ""),
('number', 'resourceNumber', 0), ("number", "resourceNumber", 0),
('label', 'labelName', ''), ("label", "labelName", ""),
] ]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(
lockable_resources, data, mapping, fail_required=True) lockable_resources, data, mapping, fail_required=True
secure_groovy_script = XML.SubElement(lockable_resources, )
'resourceMatchScript') secure_groovy_script = XML.SubElement(lockable_resources, "resourceMatchScript")
mapping = [ mapping = [("match-script", "script", None), ("groovy-sandbox", "sandbox", False)]
('match-script', 'script', None), helpers.convert_mapping_to_xml(
('groovy-sandbox', 'sandbox', False), secure_groovy_script, data, mapping, fail_required=False
] )
helpers.convert_mapping_to_xml(secure_groovy_script, data, mapping,
fail_required=False)
def docker_container(registry, xml_parent, data): def docker_container(registry, xml_parent, data):
@ -1189,24 +1149,25 @@ def docker_container(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
xml_docker = XML.SubElement( xml_docker = XML.SubElement(
xml_parent, 'com.nirima.jenkins.plugins.docker.DockerJobProperty') xml_parent, "com.nirima.jenkins.plugins.docker.DockerJobProperty"
)
registry = XML.SubElement(xml_docker, 'registry') registry = XML.SubElement(xml_docker, "registry")
registry.set('plugin', 'docker-commons') registry.set("plugin", "docker-commons")
registry_mapping = [ registry_mapping = [
('docker-registry-url', 'url', ''), ("docker-registry-url", "url", ""),
('credentials-id', 'credentialsId', ''), ("credentials-id", "credentialsId", ""),
] ]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(
registry, data, registry_mapping, fail_required=False) registry, data, registry_mapping, fail_required=False
)
mapping = [ mapping = [
('commit-on-success', 'tagOnCompletion', False), ("commit-on-success", "tagOnCompletion", False),
('additional-tag', 'additionalTag', ''), ("additional-tag", "additionalTag", ""),
('push-on-success', 'pushOnSuccess', False), ("push-on-success", "pushOnSuccess", False),
('clean-local-images', 'cleanImages', True), ("clean-local-images", "cleanImages", True),
] ]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(xml_docker, data, mapping, fail_required=True)
xml_docker, data, mapping, fail_required=True)
def disable_resume(registry, xml_parent, data): def disable_resume(registry, xml_parent, data):
@ -1222,9 +1183,10 @@ def disable_resume(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
XML.SubElement(xml_parent, XML.SubElement(
'org.jenkinsci.plugins.workflow.job.properties.' xml_parent,
'DisableResumeJobProperty') "org.jenkinsci.plugins.workflow.job.properties." "DisableResumeJobProperty",
)
def cachet_gating(registry, xml_parent, data): def cachet_gating(registry, xml_parent, data):
@ -1246,32 +1208,30 @@ def cachet_gating(registry, xml_parent, data):
:language: yaml :language: yaml
""" """
cachet = XML.SubElement( cachet = XML.SubElement(
xml_parent, 'com.redhat.jenkins.plugins.cachet.CachetJobProperty') xml_parent, "com.redhat.jenkins.plugins.cachet.CachetJobProperty"
cachet.set('plugin', 'cachet-gating') )
cachet.set("plugin", "cachet-gating")
mapping = [ mapping = [("required-resources", "requiredResources", True)]
('required-resources', 'requiredResources', True), helpers.convert_mapping_to_xml(cachet, data, mapping, fail_required=True)
]
helpers.convert_mapping_to_xml(
cachet, data, mapping, fail_required=True)
resources_data = data.get('resources', []) resources_data = data.get("resources", [])
if resources_data: if resources_data:
resources = XML.SubElement(cachet, 'resources') resources = XML.SubElement(cachet, "resources")
for resource in resources_data: for resource in resources_data:
XML.SubElement(resources, 'string').text = str(resource) XML.SubElement(resources, "string").text = str(resource)
class Properties(jenkins_jobs.modules.base.Base): class Properties(jenkins_jobs.modules.base.Base):
sequence = 20 sequence = 20
component_type = 'property' component_type = "property"
component_list_type = 'properties' component_list_type = "properties"
def gen_xml(self, xml_parent, data): def gen_xml(self, xml_parent, data):
properties = xml_parent.find('properties') properties = xml_parent.find("properties")
if properties is None: if properties is None:
properties = XML.SubElement(xml_parent, 'properties') properties = XML.SubElement(xml_parent, "properties")
for prop in data.get('properties', []): for prop in data.get("properties", []):
self.registry.dispatch('property', properties, prop) self.registry.dispatch("property", properties, prop)

File diff suppressed because it is too large Load Diff

View File

@ -57,21 +57,19 @@ def email(registry, xml_parent, data):
recipients: breakage@example.com recipients: breakage@example.com
""" """
mailer = XML.SubElement(xml_parent, mailer = XML.SubElement(xml_parent, "hudson.maven.reporters.MavenMailer")
'hudson.maven.reporters.MavenMailer') XML.SubElement(mailer, "recipients").text = data["recipients"]
XML.SubElement(mailer, 'recipients').text = data['recipients']
# Note the logic reversal (included here to match the GUI # Note the logic reversal (included here to match the GUI
if data.get('notify-every-unstable-build', True): if data.get("notify-every-unstable-build", True):
XML.SubElement(mailer, 'dontNotifyEveryUnstableBuild').text = 'false' XML.SubElement(mailer, "dontNotifyEveryUnstableBuild").text = "false"
else: else:
XML.SubElement(mailer, 'dontNotifyEveryUnstableBuild').text = 'true' XML.SubElement(mailer, "dontNotifyEveryUnstableBuild").text = "true"
mapping = [ mapping = [
('send-to-individuals', 'sendToIndividuals', False), ("send-to-individuals", "sendToIndividuals", False),
('notify-for-each-module', 'perModuleEmail', True), ("notify-for-each-module", "perModuleEmail", True),
] ]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(mailer, data, mapping, fail_required=False)
mailer, data, mapping, fail_required=False)
def findbugs(registry, xml_parent, data): def findbugs(registry, xml_parent, data):
@ -131,29 +129,29 @@ def findbugs(registry, xml_parent, data):
.. literalinclude:: /../../tests/reporters/fixtures/findbugs01.yaml .. literalinclude:: /../../tests/reporters/fixtures/findbugs01.yaml
""" """
findbugs = XML.SubElement(xml_parent, findbugs = XML.SubElement(xml_parent, "hudson.plugins.findbugs.FindBugsReporter")
'hudson.plugins.findbugs.FindBugsReporter') findbugs.set("plugin", "findbugs")
findbugs.set('plugin', 'findbugs')
helpers.findbugs_settings(findbugs, data) helpers.findbugs_settings(findbugs, data)
helpers.build_trends_publisher('[FINDBUGS] ', findbugs, data) helpers.build_trends_publisher("[FINDBUGS] ", findbugs, data)
class Reporters(jenkins_jobs.modules.base.Base): class Reporters(jenkins_jobs.modules.base.Base):
sequence = 55 sequence = 55
component_type = 'reporter' component_type = "reporter"
component_list_type = 'reporters' component_list_type = "reporters"
def gen_xml(self, xml_parent, data): def gen_xml(self, xml_parent, data):
if 'reporters' not in data: if "reporters" not in data:
return return
if xml_parent.tag != 'maven2-moduleset': if xml_parent.tag != "maven2-moduleset":
raise JenkinsJobsException("Reporters may only be used for Maven " raise JenkinsJobsException(
"modules.") "Reporters may only be used for Maven " "modules."
)
reporters = XML.SubElement(xml_parent, 'reporters') reporters = XML.SubElement(xml_parent, "reporters")
for action in data.get('reporters', []): for action in data.get("reporters", []):
self.registry.dispatch('reporter', reporters, action) self.registry.dispatch("reporter", reporters, action)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -33,17 +33,16 @@ class All(jenkins_jobs.modules.base.Base):
sequence = 0 sequence = 0
def root_xml(self, data): def root_xml(self, data):
root = XML.Element('hudson.model.AllView') root = XML.Element("hudson.model.AllView")
mapping = [ mapping = [
('name', 'name', None), ("name", "name", None),
('description', 'description', ''), ("description", "description", ""),
('filter-executors', 'filterExecutors', False), ("filter-executors", "filterExecutors", False),
('filter-queue', 'filterQueue', False), ("filter-queue", "filterQueue", False),
] ]
helpers.convert_mapping_to_xml(root, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(root, data, mapping, fail_required=True)
XML.SubElement(root, 'properties', XML.SubElement(root, "properties", {"class": "hudson.model.View$PropertyList"})
{'class': 'hudson.model.View$PropertyList'})
return root return root

View File

@ -17,195 +17,189 @@ import jenkins_jobs.modules.helpers as helpers
def build_duration(xml_parent, data): def build_duration(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.BuildDurationFilter') xml = XML.SubElement(xml_parent, "hudson.views.BuildDurationFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('match-type', 'includeExcludeTypeString', 'includeMatched'), ("match-type", "includeExcludeTypeString", "includeMatched"),
('build-duration-type', 'buildCountTypeString', 'Latest'), ("build-duration-type", "buildCountTypeString", "Latest"),
('amount-type', 'amountTypeString', 'Hours'), ("amount-type", "amountTypeString", "Hours"),
('amount', 'amount', '0'), ("amount", "amount", "0"),
('less-than', 'lessThan', True), ("less-than", "lessThan", True),
('build-duration-minutes', 'buildDurationMinutes', '0'), ("build-duration-minutes", "buildDurationMinutes", "0"),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def build_status(xml_parent, data): def build_status(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.BuildStatusFilter') xml = XML.SubElement(xml_parent, "hudson.views.BuildStatusFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('match-type', 'includeExcludeTypeString', 'includeMatched'), ("match-type", "includeExcludeTypeString", "includeMatched"),
('never-built', 'neverBuilt', False), ("never-built", "neverBuilt", False),
('building', 'building', False), ("building", "building", False),
('in-build-queue', 'inBuildQueue', False), ("in-build-queue", "inBuildQueue", False),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def build_trend(xml_parent, data): def build_trend(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.BuildTrendFilter') xml = XML.SubElement(xml_parent, "hudson.views.BuildTrendFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('match-type', 'includeExcludeTypeString', 'includeMatched'), ("match-type", "includeExcludeTypeString", "includeMatched"),
('build-trend-type', 'buildCountTypeString', 'Latest'), ("build-trend-type", "buildCountTypeString", "Latest"),
('amount-type', 'amountTypeString', 'Hours'), ("amount-type", "amountTypeString", "Hours"),
('amount', 'amount', '0'), ("amount", "amount", "0"),
('status', 'statusTypeString', 'Completed'), ("status", "statusTypeString", "Completed"),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def fallback(xml_parent, data): def fallback(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.AddRemoveFallbackFilter') xml = XML.SubElement(xml_parent, "hudson.views.AddRemoveFallbackFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('fallback-type', 'fallbackTypeString', 'REMOVE_ALL_IF_ALL_INCLUDED'), ("fallback-type", "fallbackTypeString", "REMOVE_ALL_IF_ALL_INCLUDED"),
('fallback-type', 'fallbackType', 'REMOVE_ALL_IF_ALL_INCLUDED'), ("fallback-type", "fallbackType", "REMOVE_ALL_IF_ALL_INCLUDED"),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def job_status(xml_parent, data): def job_status(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.JobStatusFilter') xml = XML.SubElement(xml_parent, "hudson.views.JobStatusFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('match-type', 'includeExcludeTypeString', 'includeMatched'), ("match-type", "includeExcludeTypeString", "includeMatched"),
('unstable', 'unstable', False), ("unstable", "unstable", False),
('failed', 'failed', False), ("failed", "failed", False),
('aborted', 'aborted', False), ("aborted", "aborted", False),
('disabled', 'disabled', False), ("disabled", "disabled", False),
('stable', 'stable', False), ("stable", "stable", False),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def job_type(xml_parent, data): def job_type(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.JobTypeFilter') xml = XML.SubElement(xml_parent, "hudson.views.JobTypeFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('match-type', 'includeExcludeTypeString', 'includeMatched'), ("match-type", "includeExcludeTypeString", "includeMatched"),
('job-type', 'jobType', 'hudson.model.FreeStyleProject'), ("job-type", "jobType", "hudson.model.FreeStyleProject"),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def most_recent(xml_parent, data): def most_recent(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.MostRecentJobsFilter') xml = XML.SubElement(xml_parent, "hudson.views.MostRecentJobsFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('max-to-include', 'maxToInclude', '0'), ("max-to-include", "maxToInclude", "0"),
('check-start-time', 'checkStartTime', False), ("check-start-time", "checkStartTime", False),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def other_views(xml_parent, data): def other_views(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.OtherViewsFilter') xml = XML.SubElement(xml_parent, "hudson.views.OtherViewsFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('match-type', 'includeExcludeTypeString', 'includeMatched'), ("match-type", "includeExcludeTypeString", "includeMatched"),
('view-name', 'otherViewName', ("view-name", "otherViewName", "&lt;select a view other than this one&gt;"),
'&lt;select a view other than this one&gt;'),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def parameter(xml_parent, data): def parameter(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.ParameterFilter') xml = XML.SubElement(xml_parent, "hudson.views.ParameterFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('match-type', 'includeExcludeTypeString', 'includeMatched'), ("match-type", "includeExcludeTypeString", "includeMatched"),
('name', 'nameRegex', ''), ("name", "nameRegex", ""),
('value', 'valueRegex', ''), ("value", "valueRegex", ""),
('description', 'descriptionRegex', ''), ("description", "descriptionRegex", ""),
('use-default', 'useDefaultValue', False), ("use-default", "useDefaultValue", False),
('match-builds-in-progress', 'matchBuildsInProgress', False), ("match-builds-in-progress", "matchBuildsInProgress", False),
('match-all-builds', 'matchAllBuilds', False), ("match-all-builds", "matchAllBuilds", False),
('max-builds-to-match', 'maxBuildsToMatch', 0), ("max-builds-to-match", "maxBuildsToMatch", 0),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def scm(xml_parent, data): def scm(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.ScmTypeFilter') xml = XML.SubElement(xml_parent, "hudson.views.ScmTypeFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('match-type', 'includeExcludeTypeString', 'includeMatched'), ("match-type", "includeExcludeTypeString", "includeMatched"),
('scm-type', 'scmType', 'hudson.scm.NullSCM'), ("scm-type", "scmType", "hudson.scm.NullSCM"),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def secured_job(xml_parent, data): def secured_job(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.SecuredJobsFilter') xml = XML.SubElement(xml_parent, "hudson.views.SecuredJobsFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [("match-type", "includeExcludeTypeString", "includeMatched")]
('match-type', 'includeExcludeTypeString', 'includeMatched'),
]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def regex_job(xml_parent, data): def regex_job(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.RegExJobFilter') xml = XML.SubElement(xml_parent, "hudson.views.RegExJobFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('match-type', 'includeExcludeTypeString', 'includeMatched'), ("match-type", "includeExcludeTypeString", "includeMatched"),
('regex-name', 'valueTypeString', ''), ("regex-name", "valueTypeString", ""),
('regex', 'regex', ''), ("regex", "regex", ""),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def unclassified(xml_parent, data): def unclassified(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.UnclassifiedJobsFilter') xml = XML.SubElement(xml_parent, "hudson.views.UnclassifiedJobsFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [("match-type", "includeExcludeTypeString", "includeMatched")]
('match-type', 'includeExcludeTypeString', 'includeMatched'),
]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def upstream_downstream(xml_parent, data): def upstream_downstream(xml_parent, data):
xml = XML.SubElement( xml = XML.SubElement(xml_parent, "hudson.views.UpstreamDownstreamJobsFilter")
xml_parent, 'hudson.views.UpstreamDownstreamJobsFilter') xml.set("plugin", "view-job-filters")
xml.set('plugin', 'view-job-filters')
mapping = [ mapping = [
('include-upstream', 'includeUpstream', False), ("include-upstream", "includeUpstream", False),
('include-downstream', 'includeDownstream', False), ("include-downstream", "includeDownstream", False),
('recursive', 'recursive', False), ("recursive", "recursive", False),
('exclude-originals', 'excludeOriginals', False), ("exclude-originals", "excludeOriginals", False),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def user_permissions(xml_parent, data): def user_permissions(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.SecurityFilter') xml = XML.SubElement(xml_parent, "hudson.views.SecurityFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('match-type', 'includeExcludeTypeString', 'includeMatched'), ("match-type", "includeExcludeTypeString", "includeMatched"),
('configure', 'configure', False), ("configure", "configure", False),
('build', 'build', False), ("build", "build", False),
('workspace', 'workspace', False), ("workspace", "workspace", False),
('permission-check', 'permissionCheckType', 'MustMatchAll'), ("permission-check", "permissionCheckType", "MustMatchAll"),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)
def user_relevence(xml_parent, data): def user_relevence(xml_parent, data):
xml = XML.SubElement(xml_parent, 'hudson.views.UserRelevanceFilter') xml = XML.SubElement(xml_parent, "hudson.views.UserRelevanceFilter")
xml.set('plugin', 'view-job-filters') xml.set("plugin", "view-job-filters")
mapping = [ mapping = [
('match-type', 'includeExcludeTypeString', 'includeMatched'), ("match-type", "includeExcludeTypeString", "includeMatched"),
('build-count', 'buildCountTypeString', 'AtLeastOne'), ("build-count", "buildCountTypeString", "AtLeastOne"),
('amount-type', 'amountTypeString', 'Hours'), ("amount-type", "amountTypeString", "Hours"),
('amount', 'amount', '0'), ("amount", "amount", "0"),
('match-user-id', 'matchUserId', False), ("match-user-id", "matchUserId", False),
('match-user-fullname', 'matchUserFullName', False), ("match-user-fullname", "matchUserFullName", False),
('ignore-case', 'ignoreCase', False), ("ignore-case", "ignoreCase", False),
('ignore-whitespace', 'ignoreWhitespace', False), ("ignore-whitespace", "ignoreWhitespace", False),
('ignore-non-alphaNumeric', 'ignoreNonAlphaNumeric', False), ("ignore-non-alphaNumeric", "ignoreNonAlphaNumeric", False),
('match-builder', 'matchBuilder', False), ("match-builder", "matchBuilder", False),
('match-email', 'matchEmail', False), ("match-email", "matchEmail", False),
('match-scm-changes', 'matchScmChanges', False), ("match-scm-changes", "matchScmChanges", False),
] ]
helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(xml, data, mapping, fail_required=True)

View File

@ -227,147 +227,153 @@ import jenkins_jobs.modules.helpers as helpers
import jenkins_jobs.modules.view_jobfilters as view_jobfilters import jenkins_jobs.modules.view_jobfilters as view_jobfilters
COLUMN_DICT = { COLUMN_DICT = {
'status': 'hudson.views.StatusColumn', "status": "hudson.views.StatusColumn",
'weather': 'hudson.views.WeatherColumn', "weather": "hudson.views.WeatherColumn",
'job': 'hudson.views.JobColumn', "job": "hudson.views.JobColumn",
'last-success': 'hudson.views.LastSuccessColumn', "last-success": "hudson.views.LastSuccessColumn",
'last-failure': 'hudson.views.LastFailureColumn', "last-failure": "hudson.views.LastFailureColumn",
'last-duration': 'hudson.views.LastDurationColumn', "last-duration": "hudson.views.LastDurationColumn",
'build-button': 'hudson.views.BuildButtonColumn', "build-button": "hudson.views.BuildButtonColumn",
'last-stable': 'hudson.views.LastStableColumn', "last-stable": "hudson.views.LastStableColumn",
'robot-list': 'hudson.plugins.robot.view.RobotListViewColumn', "robot-list": "hudson.plugins.robot.view.RobotListViewColumn",
'find-bugs': 'hudson.plugins.findbugs.FindBugsColumn', "find-bugs": "hudson.plugins.findbugs.FindBugsColumn",
'jacoco': 'hudson.plugins.jacococoveragecolumn.JaCoCoColumn', "jacoco": "hudson.plugins.jacococoveragecolumn.JaCoCoColumn",
'git-branch': 'hudson.plugins.git.GitBranchSpecifierColumn', "git-branch": "hudson.plugins.git.GitBranchSpecifierColumn",
'schedule-build': "schedule-build": "org.jenkinsci.plugins.schedulebuild.ScheduleBuildButtonColumn",
'org.jenkinsci.plugins.schedulebuild.ScheduleBuildButtonColumn', "priority-sorter": "jenkins.advancedqueue.PrioritySorterJobColumn",
'priority-sorter': 'jenkins.advancedqueue.PrioritySorterJobColumn', "build-filter": "hudson.views.BuildFilterColumn",
'build-filter': 'hudson.views.BuildFilterColumn', "desc": "jenkins.branch.DescriptionColumn",
'desc': 'jenkins.branch.DescriptionColumn', "policy-violations": "com.sonatype.insight.ci.hudson.QualityColumn "
'policy-violations': 'plugin="sonatype-clm-ci"',
'com.sonatype.insight.ci.hudson.QualityColumn ' "member-graph-view": "com.barchart.jenkins.cascade.GraphViewColumn "
'plugin="sonatype-clm-ci"', 'plugin="maven-release-cascade"',
'member-graph-view': "extra-tests-total": [
'com.barchart.jenkins.cascade.GraphViewColumn ' ["jenkins.plugins.extracolumns.TestResultColumn", {"plugin": "extra-columns"}],
'plugin="maven-release-cascade"', "<testResultFormat>2</testResultFormat>",
'extra-tests-total': [ ],
['jenkins.plugins.extracolumns.TestResultColumn', "extra-tests-failed": [
{'plugin': 'extra-columns'}], ["jenkins.plugins.extracolumns.TestResultColumn", {"plugin": "extra-columns"}],
'<testResultFormat>2</testResultFormat>'], "<testResultFormat>3</testResultFormat>",
'extra-tests-failed': [ ],
['jenkins.plugins.extracolumns.TestResultColumn', "extra-tests-passed": [
{'plugin': 'extra-columns'}], ["jenkins.plugins.extracolumns.TestResultColumn", {"plugin": "extra-columns"}],
'<testResultFormat>3</testResultFormat>'], "<testResultFormat>4</testResultFormat>",
'extra-tests-passed': [ ],
['jenkins.plugins.extracolumns.TestResultColumn', "extra-tests-skipped": [
{'plugin': 'extra-columns'}], ["jenkins.plugins.extracolumns.TestResultColumn", {"plugin": "extra-columns"}],
'<testResultFormat>4</testResultFormat>'], "<testResultFormat>5</testResultFormat>",
'extra-tests-skipped': [ ],
['jenkins.plugins.extracolumns.TestResultColumn', "extra-tests-format-0": [
{'plugin': 'extra-columns'}], ["jenkins.plugins.extracolumns.TestResultColumn", {"plugin": "extra-columns"}],
'<testResultFormat>5</testResultFormat>'], "<testResultFormat>0</testResultFormat>",
'extra-tests-format-0': [ ],
['jenkins.plugins.extracolumns.TestResultColumn', "extra-tests-format-1": [
{'plugin': 'extra-columns'}], ["jenkins.plugins.extracolumns.TestResultColumn", {"plugin": "extra-columns"}],
'<testResultFormat>0</testResultFormat>'], "<testResultFormat>1</testResultFormat>",
'extra-tests-format-1': [ ],
['jenkins.plugins.extracolumns.TestResultColumn', "extra-build-description": [
{'plugin': 'extra-columns'}], [
'<testResultFormat>1</testResultFormat>'], "jenkins.plugins.extracolumns.BuildDescriptionColumn",
'extra-build-description': [ {"plugin": "extra-columns"},
['jenkins.plugins.extracolumns.BuildDescriptionColumn', ],
{'plugin': 'extra-columns'}], "<columnWidth>3</columnWidth>",
'<columnWidth>3</columnWidth>', '<forceWidth>false</forceWidth>'], "<forceWidth>false</forceWidth>",
'extra-build-parameters': [ ],
['jenkins.plugins.extracolumns.BuildParametersColumn', "extra-build-parameters": [
{'plugin': 'extra-columns'}], [
'<singlePara>false</singlePara>', '<parameterName/>'], "jenkins.plugins.extracolumns.BuildParametersColumn",
'extra-last-user-name': {"plugin": "extra-columns"},
'jenkins.plugins.extracolumns.UserNameColumn' ],
' plugin="extra-columns"', "<singlePara>false</singlePara>",
'extra-last-output': "<parameterName/>",
'jenkins.plugins.extracolumns.LastBuildConsoleColumn' ],
' plugin="extra-columns"', "extra-last-user-name": "jenkins.plugins.extracolumns.UserNameColumn"
'extra-workspace-link': ' plugin="extra-columns"',
'jenkins.plugins.extracolumns.WorkspaceColumn ' "extra-last-output": "jenkins.plugins.extracolumns.LastBuildConsoleColumn"
'plugin="extra-columns"', ' plugin="extra-columns"',
'extra-configure-button': "extra-workspace-link": "jenkins.plugins.extracolumns.WorkspaceColumn "
'jenkins.plugins.extracolumns.ConfigureProjectColumn' 'plugin="extra-columns"',
' plugin="extra-columns"', "extra-configure-button": "jenkins.plugins.extracolumns.ConfigureProjectColumn"
' plugin="extra-columns"',
} }
DEFAULT_COLUMNS = ['status', 'weather', 'job', 'last-success', 'last-failure', DEFAULT_COLUMNS = [
'last-duration', 'build-button'] "status",
"weather",
"job",
"last-success",
"last-failure",
"last-duration",
"build-button",
]
class List(jenkins_jobs.modules.base.Base): class List(jenkins_jobs.modules.base.Base):
sequence = 0 sequence = 0
def root_xml(self, data): def root_xml(self, data):
root = XML.Element('hudson.model.ListView') root = XML.Element("hudson.model.ListView")
mapping = [ mapping = [
('name', 'name', None), ("name", "name", None),
('description', 'description', ''), ("description", "description", ""),
('filter-executors', 'filterExecutors', False), ("filter-executors", "filterExecutors", False),
('filter-queue', 'filterQueue', False), ("filter-queue", "filterQueue", False),
] ]
helpers.convert_mapping_to_xml(root, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(root, data, mapping, fail_required=True)
XML.SubElement(root, 'properties', XML.SubElement(root, "properties", {"class": "hudson.model.View$PropertyList"})
{'class': 'hudson.model.View$PropertyList'})
jn_xml = XML.SubElement(root, 'jobNames') jn_xml = XML.SubElement(root, "jobNames")
jobnames = data.get('job-name', None) jobnames = data.get("job-name", None)
XML.SubElement( XML.SubElement(
jn_xml, jn_xml, "comparator", {"class": "hudson.util.CaseInsensitiveComparator"}
'comparator', {
'class': 'hudson.util.CaseInsensitiveComparator'
}
) )
if jobnames is not None: if jobnames is not None:
# Job names must be sorted in the xml # Job names must be sorted in the xml
jobnames = sorted(jobnames, key=str.lower) jobnames = sorted(jobnames, key=str.lower)
for jobname in jobnames: for jobname in jobnames:
XML.SubElement(jn_xml, 'string').text = str(jobname) XML.SubElement(jn_xml, "string").text = str(jobname)
job_filter_xml = XML.SubElement(root, 'jobFilters') job_filter_xml = XML.SubElement(root, "jobFilters")
jobfilters = data.get('job-filters', []) jobfilters = data.get("job-filters", [])
for jobfilter in jobfilters: for jobfilter in jobfilters:
filter = getattr(view_jobfilters, jobfilter.replace('-', '_')) filter = getattr(view_jobfilters, jobfilter.replace("-", "_"))
filter(job_filter_xml, jobfilters.get(jobfilter)) filter(job_filter_xml, jobfilters.get(jobfilter))
c_xml = XML.SubElement(root, 'columns') c_xml = XML.SubElement(root, "columns")
columns = data.get('columns', DEFAULT_COLUMNS) columns = data.get("columns", DEFAULT_COLUMNS)
for column in columns: for column in columns:
if isinstance(column, dict): if isinstance(column, dict):
if 'extra-build-parameter' in column: if "extra-build-parameter" in column:
p_name = column['extra-build-parameter'] p_name = column["extra-build-parameter"]
x = XML.SubElement( x = XML.SubElement(
c_xml, c_xml,
'jenkins.plugins.extracolumns.BuildParametersColumn', "jenkins.plugins.extracolumns.BuildParametersColumn",
plugin='extra-columns' plugin="extra-columns",
)
x.append(XML.fromstring("<singlePara>true</singlePara>"))
x.append(
XML.fromstring("<parameterName>%s</parameterName>" % p_name)
) )
x.append(XML.fromstring(
'<singlePara>true</singlePara>'))
x.append(XML.fromstring(
'<parameterName>%s</parameterName>' % p_name))
else: else:
if column in COLUMN_DICT: if column in COLUMN_DICT:
if isinstance(COLUMN_DICT[column], list): if isinstance(COLUMN_DICT[column], list):
x = XML.SubElement(c_xml, COLUMN_DICT[column][0][0], x = XML.SubElement(
**COLUMN_DICT[column][0][1]) c_xml,
COLUMN_DICT[column][0][0],
**COLUMN_DICT[column][0][1]
)
for tag in COLUMN_DICT[column][1:]: for tag in COLUMN_DICT[column][1:]:
x.append(XML.fromstring(tag)) x.append(XML.fromstring(tag))
else: else:
XML.SubElement(c_xml, COLUMN_DICT[column]) XML.SubElement(c_xml, COLUMN_DICT[column])
mapping = [ mapping = [
('regex', 'includeRegex', None), ("regex", "includeRegex", None),
('recurse', 'recurse', False), ("recurse", "recurse", False),
('status-filter', 'statusFilter', None), ("status-filter", "statusFilter", None),
] ]
helpers.convert_mapping_to_xml( helpers.convert_mapping_to_xml(root, data, mapping, fail_required=False)
root, data, mapping, fail_required=False)
return root return root

View File

@ -69,43 +69,45 @@ class Pipeline(jenkins_jobs.modules.base.Base):
sequence = 0 sequence = 0
def root_xml(self, data): def root_xml(self, data):
linktypes = ['Lightbox', 'New Window'] linktypes = ["Lightbox", "New Window"]
root = XML.Element('au.com.centrumsystems.hudson.' root = XML.Element(
'plugin.buildpipeline.BuildPipelineView', "au.com.centrumsystems.hudson." "plugin.buildpipeline.BuildPipelineView",
{'plugin': 'build-pipeline-plugin'}) {"plugin": "build-pipeline-plugin"},
)
mapping_optional = [ mapping_optional = [
('description', 'description', None), ("description", "description", None),
('filter-executors', 'filterExecutors', False), ("filter-executors", "filterExecutors", False),
('filter-queue', 'filterQueue', False), ("filter-queue", "filterQueue", False),
] ]
helpers.convert_mapping_to_xml(root, data, helpers.convert_mapping_to_xml(
mapping_optional, fail_required=False) root, data, mapping_optional, fail_required=False
)
XML.SubElement(root, 'properties', XML.SubElement(root, "properties", {"class": "hudson.model.View$PropertyList"})
{'class': 'hudson.model.View$PropertyList'})
GBurl = ('au.com.centrumsystems.hudson.plugin.buildpipeline.' GBurl = (
'DownstreamProjectGridBuilder') "au.com.centrumsystems.hudson.plugin.buildpipeline."
gridBuilder = XML.SubElement(root, 'gridBuilder', {'class': GBurl}) "DownstreamProjectGridBuilder"
)
gridBuilder = XML.SubElement(root, "gridBuilder", {"class": GBurl})
jobname = data.get('first-job', '') jobname = data.get("first-job", "")
XML.SubElement(gridBuilder, 'firstJob').text = jobname XML.SubElement(gridBuilder, "firstJob").text = jobname
mapping = [ mapping = [
('name', 'name', None), ("name", "name", None),
('no-of-displayed-builds', 'noOfDisplayedBuilds', 1), ("no-of-displayed-builds", "noOfDisplayedBuilds", 1),
('title', 'buildViewTitle', ''), ("title", "buildViewTitle", ""),
('link-style', 'consoleOutputLinkStyle', 'Lightbox', linktypes), ("link-style", "consoleOutputLinkStyle", "Lightbox", linktypes),
('css-Url', 'cssUrl', ''), ("css-Url", "cssUrl", ""),
('latest-job-only', 'triggerOnlyLatestJob', False), ("latest-job-only", "triggerOnlyLatestJob", False),
('manual-trigger', 'alwaysAllowManualTrigger', False), ("manual-trigger", "alwaysAllowManualTrigger", False),
('show-parameters', 'showPipelineParameters', False), ("show-parameters", "showPipelineParameters", False),
('parameters-in-headers', ("parameters-in-headers", "showPipelineParametersInHeaders", False),
'showPipelineParametersInHeaders', False), ("start-with-parameters", "startsWithParameters", False),
('start-with-parameters', 'startsWithParameters', False), ("refresh-frequency", "refreshFrequency", 3),
('refresh-frequency', 'refreshFrequency', 3), ("definition-header", "showPipelineDefinitionHeader", False),
('definition-header', 'showPipelineDefinitionHeader', False),
] ]
helpers.convert_mapping_to_xml(root, data, mapping, fail_required=True) helpers.convert_mapping_to_xml(root, data, mapping, fail_required=True)

File diff suppressed because it is too large Load Diff

View File

@ -58,80 +58,129 @@ def zuul_post():
ZUUL_PARAMETERS = [ ZUUL_PARAMETERS = [
{'string': {
{'description': 'Zuul provided key to link builds with Gerrit events', "string": {
'name': 'ZUUL_UUID'}}, "description": "Zuul provided key to link builds with Gerrit events",
{'string': "name": "ZUUL_UUID",
{'description': 'Zuul provided key to link builds with Gerrit' }
' events (deprecated use ZUUL_UUID instead)', },
'name': 'UUID'}}, {
{'string': "string": {
{'description': 'Zuul pipeline triggering this job', "description": "Zuul provided key to link builds with Gerrit"
'name': 'ZUUL_PIPELINE'}}, " events (deprecated use ZUUL_UUID instead)",
{'string': "name": "UUID",
{'description': 'URL of Zuul\'s git repos accessible to workers', }
'name': 'ZUUL_URL'}}, },
{'string': {
{'description': 'Branch name of triggering project', "string": {
'name': 'ZUUL_PROJECT'}}, "description": "Zuul pipeline triggering this job",
{'string': "name": "ZUUL_PIPELINE",
{'description': 'Branch name of triggering change', }
'name': 'ZUUL_BRANCH'}}, },
{'string': {
{'description': 'List of dependent changes to merge', "string": {
'name': 'ZUUL_CHANGES'}}, "description": "URL of Zuul's git repos accessible to workers",
{'string': "name": "ZUUL_URL",
{'description': 'Reference for the merged commit(s) to use', }
'name': 'ZUUL_REF'}}, },
{'string': {
{'description': 'The commit SHA1 at the head of ZUUL_REF', "string": {
'name': 'ZUUL_COMMIT'}}, "description": "Branch name of triggering project",
{'string': "name": "ZUUL_PROJECT",
{'description': 'List of included changes', }
'name': 'ZUUL_CHANGE_IDS'}}, },
{'string': {
{'description': 'ID of triggering change', "string": {
'name': 'ZUUL_CHANGE'}}, "description": "Branch name of triggering change",
{'string': "name": "ZUUL_BRANCH",
{'description': 'Patchset of triggering change', }
'name': 'ZUUL_PATCHSET'}}, },
{'string': {
{'description': 'Zuul considered this job voting or not', "string": {
'name': 'ZUUL_VOTING'}}, "description": "List of dependent changes to merge",
"name": "ZUUL_CHANGES",
}
},
{
"string": {
"description": "Reference for the merged commit(s) to use",
"name": "ZUUL_REF",
}
},
{
"string": {
"description": "The commit SHA1 at the head of ZUUL_REF",
"name": "ZUUL_COMMIT",
}
},
{"string": {"description": "List of included changes", "name": "ZUUL_CHANGE_IDS"}},
{"string": {"description": "ID of triggering change", "name": "ZUUL_CHANGE"}},
{
"string": {
"description": "Patchset of triggering change",
"name": "ZUUL_PATCHSET",
}
},
{
"string": {
"description": "Zuul considered this job voting or not",
"name": "ZUUL_VOTING",
}
},
] ]
ZUUL_POST_PARAMETERS = [ ZUUL_POST_PARAMETERS = [
{'string': {
{'description': 'Zuul provided key to link builds with Gerrit events', "string": {
'name': 'ZUUL_UUID'}}, "description": "Zuul provided key to link builds with Gerrit events",
{'string': "name": "ZUUL_UUID",
{'description': 'Zuul provided key to link builds with Gerrit' }
' events (deprecated use ZUUL_UUID instead)', },
'name': 'UUID'}}, {
{'string': "string": {
{'description': 'Zuul pipeline triggering this job', "description": "Zuul provided key to link builds with Gerrit"
'name': 'ZUUL_PIPELINE'}}, " events (deprecated use ZUUL_UUID instead)",
{'string': "name": "UUID",
{'description': 'URL of Zuul\'s git repos accessible to workers', }
'name': 'ZUUL_URL'}}, },
{'string': {
{'description': 'Branch name of triggering project', "string": {
'name': 'ZUUL_PROJECT'}}, "description": "Zuul pipeline triggering this job",
{'string': "name": "ZUUL_PIPELINE",
{'description': 'Name of updated reference triggering this job', }
'name': 'ZUUL_REF'}}, },
{'string': {
{'description': 'Name of updated reference triggering this job', "string": {
'name': 'ZUUL_REFNAME'}}, "description": "URL of Zuul's git repos accessible to workers",
{'string': "name": "ZUUL_URL",
{'description': 'Old SHA at this reference', }
'name': 'ZUUL_OLDREV'}}, },
{'string': {
{'description': 'New SHA at this reference', "string": {
'name': 'ZUUL_NEWREV'}}, "description": "Branch name of triggering project",
{'string': "name": "ZUUL_PROJECT",
{'description': 'Shortened new SHA at this reference', }
'name': 'ZUUL_SHORT_NEWREV'}}, },
{
"string": {
"description": "Name of updated reference triggering this job",
"name": "ZUUL_REF",
}
},
{
"string": {
"description": "Name of updated reference triggering this job",
"name": "ZUUL_REFNAME",
}
},
{"string": {"description": "Old SHA at this reference", "name": "ZUUL_OLDREV"}},
{"string": {"description": "New SHA at this reference", "name": "ZUUL_NEWREV"}},
{
"string": {
"description": "Shortened new SHA at this reference",
"name": "ZUUL_SHORT_NEWREV",
}
},
] ]
@ -141,23 +190,24 @@ class Zuul(jenkins_jobs.modules.base.Base):
def handle_data(self, job_data): def handle_data(self, job_data):
changed = False changed = False
jobs = itertools.chain( jobs = itertools.chain(
job_data.get('job', {}).values(), job_data.get("job", {}).values(), job_data.get("job-template", {}).values()
job_data.get('job-template', {}).values()) )
for job in jobs: for job in jobs:
triggers = job.get('triggers') triggers = job.get("triggers")
if not triggers: if not triggers:
continue continue
if ('zuul' not in job.get('triggers', []) and if "zuul" not in job.get("triggers", []) and "zuul-post" not in job.get(
'zuul-post' not in job.get('triggers', [])): "triggers", []
):
continue continue
if 'parameters' not in job: if "parameters" not in job:
job['parameters'] = [] job["parameters"] = []
if 'zuul' in job.get('triggers', []): if "zuul" in job.get("triggers", []):
job['parameters'].extend(ZUUL_PARAMETERS) job["parameters"].extend(ZUUL_PARAMETERS)
job['triggers'].remove('zuul') job["triggers"].remove("zuul")
if 'zuul-post' in job.get('triggers', []): if "zuul-post" in job.get("triggers", []):
job['parameters'].extend(ZUUL_POST_PARAMETERS) job["parameters"].extend(ZUUL_POST_PARAMETERS)
job['triggers'].remove('zuul-post') job["triggers"].remove("zuul-post")
changed = True changed = True
return changed return changed

View File

@ -33,11 +33,12 @@ class TaskFunc(dict):
""" """
Simple class to wrap around the information needed to run a function. Simple class to wrap around the information needed to run a function.
""" """
def __init__(self, n_ord, func, args=None, kwargs=None): def __init__(self, n_ord, func, args=None, kwargs=None):
self['func'] = func self["func"] = func
self['args'] = args or [] self["args"] = args or []
self['kwargs'] = kwargs or {} self["kwargs"] = kwargs or {}
self['ord'] = n_ord self["ord"] = n_ord
class Worker(threading.Thread): class Worker(threading.Thread):
@ -47,6 +48,7 @@ class Worker(threading.Thread):
If the string 'done' is passed instead of a TaskFunc instance, the thread If the string 'done' is passed instead of a TaskFunc instance, the thread
will end. will end.
""" """
def __init__(self, in_queue, out_queue): def __init__(self, in_queue, out_queue):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.in_queue = in_queue self.in_queue = in_queue
@ -55,15 +57,14 @@ class Worker(threading.Thread):
def run(self): def run(self):
while True: while True:
task = self.in_queue.get() task = self.in_queue.get()
if task == 'done': if task == "done":
return return
try: try:
res = task['func'](*task['args'], res = task["func"](*task["args"], **task["kwargs"])
**task['kwargs'])
except Exception as exc: except Exception as exc:
res = exc res = exc
traceback.print_exc() traceback.print_exc()
self.out_queue.put((task['ord'], res)) self.out_queue.put((task["ord"], res))
def concurrent(func): def concurrent(func):
@ -102,8 +103,8 @@ def concurrent(func):
array with the results of the executions in the same order the array with the results of the executions in the same order the
parameters were passed. parameters were passed.
""" """
n_workers = kwargs.pop('n_workers', 0) n_workers = kwargs.pop("n_workers", 0)
p_kwargs = kwargs.pop('concurrent', []) p_kwargs = kwargs.pop("concurrent", [])
# if only one parameter is passed inside the concurrent dict, run the # if only one parameter is passed inside the concurrent dict, run the
# original function as is, no need for pools # original function as is, no need for pools
if len(p_kwargs) == 1: if len(p_kwargs) == 1:
@ -133,7 +134,7 @@ def concurrent(func):
in_queue.put(TaskFunc(n_ord, func, args, f_kwargs)) in_queue.put(TaskFunc(n_ord, func, args, f_kwargs))
n_ord += 1 n_ord += 1
for _ in range(n_workers): for _ in range(n_workers):
in_queue.put('done') in_queue.put("done")
# Wait for the results # Wait for the results
logging.debug("Waiting for workers to finish processing") logging.debug("Waiting for workers to finish processing")
@ -148,4 +149,5 @@ def concurrent(func):
results = [r[1] for r in sorted(results)] results = [r[1] for r in sorted(results)]
logging.debug("Concurrent task finished") logging.debug("Concurrent task finished")
return results return results
return concurrentized return concurrentized

View File

@ -29,9 +29,7 @@ from jenkins_jobs.formatter import deep_format
import jenkins_jobs.local_yaml as local_yaml import jenkins_jobs.local_yaml as local_yaml
from jenkins_jobs import utils from jenkins_jobs import utils
__all__ = [ __all__ = ["YamlParser"]
"YamlParser"
]
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -45,8 +43,7 @@ def matches(what, glob_patterns):
:arg iterable glob_patterns: glob patterns to match (list, tuple, set, :arg iterable glob_patterns: glob patterns to match (list, tuple, set,
etc.) etc.)
""" """
return any(fnmatch.fnmatch(what, glob_pattern) return any(fnmatch.fnmatch(what, glob_pattern) for glob_pattern in glob_patterns)
for glob_pattern in glob_patterns)
def combination_matches(combination, match_combinations): def combination_matches(combination, match_combinations):
@ -79,28 +76,32 @@ class YamlParser(object):
self.views = [] self.views = []
self.jjb_config = jjb_config self.jjb_config = jjb_config
self.keep_desc = jjb_config.yamlparser['keep_descriptions'] self.keep_desc = jjb_config.yamlparser["keep_descriptions"]
self.path = jjb_config.yamlparser['include_path'] self.path = jjb_config.yamlparser["include_path"]
def load_files(self, fn): def load_files(self, fn):
# handle deprecated behavior, and check that it's not a file like # handle deprecated behavior, and check that it's not a file like
# object as these may implement the '__iter__' attribute. # object as these may implement the '__iter__' attribute.
if not hasattr(fn, '__iter__') or hasattr(fn, 'read'): if not hasattr(fn, "__iter__") or hasattr(fn, "read"):
logger.warning( logger.warning(
'Passing single elements for the `fn` argument in ' "Passing single elements for the `fn` argument in "
'Builder.load_files is deprecated. Please update your code ' "Builder.load_files is deprecated. Please update your code "
'to use a list as support for automatic conversion will be ' "to use a list as support for automatic conversion will be "
'removed in a future version.') "removed in a future version."
)
fn = [fn] fn = [fn]
files_to_process = [] files_to_process = []
for path in fn: for path in fn:
if not hasattr(path, 'read') and os.path.isdir(path): if not hasattr(path, "read") and os.path.isdir(path):
files_to_process.extend([os.path.join(path, f) files_to_process.extend(
for f in sorted(os.listdir(path)) [
if (f.endswith('.yml') or os.path.join(path, f)
f.endswith('.yaml'))]) for f in sorted(os.listdir(path))
if (f.endswith(".yml") or f.endswith(".yaml"))
]
)
else: else:
files_to_process.append(path) files_to_process.append(path)
@ -108,41 +109,45 @@ class YamlParser(object):
# definitions of macros and templates when loading all from top-level # definitions of macros and templates when loading all from top-level
unique_files = [] unique_files = []
for f in files_to_process: for f in files_to_process:
if hasattr(f, 'read'): if hasattr(f, "read"):
unique_files.append(f) unique_files.append(f)
continue continue
rpf = os.path.realpath(f) rpf = os.path.realpath(f)
if rpf not in unique_files: if rpf not in unique_files:
unique_files.append(rpf) unique_files.append(rpf)
else: else:
logger.warning("File '%s' already added as '%s', ignoring " logger.warning(
"reference to avoid duplicating yaml " "File '%s' already added as '%s', ignoring "
"definitions." % (f, rpf)) "reference to avoid duplicating yaml "
"definitions." % (f, rpf)
)
for in_file in unique_files: for in_file in unique_files:
# use of ask-for-permissions instead of ask-for-forgiveness # use of ask-for-permissions instead of ask-for-forgiveness
# performs better when low use cases. # performs better when low use cases.
if hasattr(in_file, 'name'): if hasattr(in_file, "name"):
fname = in_file.name fname = in_file.name
else: else:
fname = in_file fname = in_file
logger.debug("Parsing YAML file {0}".format(fname)) logger.debug("Parsing YAML file {0}".format(fname))
if hasattr(in_file, 'read'): if hasattr(in_file, "read"):
self._parse_fp(in_file) self._parse_fp(in_file)
else: else:
self.parse(in_file) self.parse(in_file)
def _parse_fp(self, fp): def _parse_fp(self, fp):
# wrap provided file streams to ensure correct encoding used # wrap provided file streams to ensure correct encoding used
data = local_yaml.load(utils.wrap_stream(fp), data = local_yaml.load(
self.jjb_config.yamlparser['retain_anchors'], utils.wrap_stream(fp),
search_path=self.path) self.jjb_config.yamlparser["retain_anchors"],
search_path=self.path,
)
if data: if data:
if not isinstance(data, list): if not isinstance(data, list):
raise JenkinsJobsException( raise JenkinsJobsException(
"The topmost collection in file '{fname}' must be a list," "The topmost collection in file '{fname}' must be a list,"
" not a {cls}".format(fname=getattr(fp, 'name', fp), " not a {cls}".format(fname=getattr(fp, "name", fp), cls=type(data))
cls=type(data))) )
for item in data: for item in data:
cls, dfn = next(iter(item.items())) cls, dfn = next(iter(item.items()))
group = self.data.get(cls, {}) group = self.data.get(cls, {})
@ -153,41 +158,43 @@ class YamlParser(object):
n = v n = v
break break
# Syntax error # Syntax error
raise JenkinsJobsException("Syntax error, for item " raise JenkinsJobsException(
"named '{0}'. Missing indent?" "Syntax error, for item "
.format(n)) "named '{0}'. Missing indent?".format(n)
)
# allow any entry to specify an id that can also be used # allow any entry to specify an id that can also be used
_id = dfn.get('id', dfn['name']) _id = dfn.get("id", dfn["name"])
if _id in group: if _id in group:
self._handle_dups( self._handle_dups(
"Duplicate entry found in '{0}: '{1}' already " "Duplicate entry found in '{0}: '{1}' already "
"defined".format(fp.name, _id)) "defined".format(fp.name, _id)
)
group[_id] = dfn group[_id] = dfn
self.data[cls] = group self.data[cls] = group
def parse(self, fn): def parse(self, fn):
with io.open(fn, 'r', encoding='utf-8') as fp: with io.open(fn, "r", encoding="utf-8") as fp:
self._parse_fp(fp) self._parse_fp(fp)
def _handle_dups(self, message): def _handle_dups(self, message):
if not self.jjb_config.yamlparser['allow_duplicates']: if not self.jjb_config.yamlparser["allow_duplicates"]:
logger.error(message) logger.error(message)
raise JenkinsJobsException(message) raise JenkinsJobsException(message)
else: else:
logger.warning(message) logger.warning(message)
def _getJob(self, name): def _getJob(self, name):
job = self.data.get('job', {}).get(name, None) job = self.data.get("job", {}).get(name, None)
if not job: if not job:
return job return job
return self._applyDefaults(job) return self._applyDefaults(job)
def _getJobGroup(self, name): def _getJobGroup(self, name):
return self.data.get('job-group', {}).get(name, None) return self.data.get("job-group", {}).get(name, None)
def _getJobTemplate(self, name): def _getJobTemplate(self, name):
job = self.data.get('job-template', {}).get(name, None) job = self.data.get("job-template", {}).get(name, None)
if not job: if not job:
return job return job
return self._applyDefaults(job) return self._applyDefaults(job)
@ -196,12 +203,12 @@ class YamlParser(object):
if override_dict is None: if override_dict is None:
override_dict = {} override_dict = {}
whichdefaults = data.get('defaults', 'global') whichdefaults = data.get("defaults", "global")
defaults = copy.deepcopy(self.data.get('defaults', defaults = copy.deepcopy(self.data.get("defaults", {}).get(whichdefaults, {}))
{}).get(whichdefaults, {})) if defaults == {} and whichdefaults != "global":
if defaults == {} and whichdefaults != 'global': raise JenkinsJobsException(
raise JenkinsJobsException("Unknown defaults set: '{0}'" "Unknown defaults set: '{0}'".format(whichdefaults)
.format(whichdefaults)) )
for key in override_dict.keys(): for key in override_dict.keys():
if key in defaults.keys(): if key in defaults.keys():
@ -216,53 +223,52 @@ class YamlParser(object):
if self.keep_desc: if self.keep_desc:
description = job.get("description", None) description = job.get("description", None)
else: else:
description = job.get("description", '') description = job.get("description", "")
if description is not None: if description is not None:
job["description"] = description + \ job["description"] = description + self._get_managed_string().lstrip()
self._get_managed_string().lstrip()
def _getfullname(self, data): def _getfullname(self, data):
if 'folder' in data: if "folder" in data:
return "%s/%s" % (data['folder'], data['name']) return "%s/%s" % (data["folder"], data["name"])
return data['name'] return data["name"]
def expandYaml(self, registry, jobs_glob=None): def expandYaml(self, registry, jobs_glob=None):
changed = True changed = True
while changed: while changed:
changed = False changed = False
for module in registry.modules: for module in registry.modules:
if hasattr(module, 'handle_data'): if hasattr(module, "handle_data"):
if module.handle_data(self.data): if module.handle_data(self.data):
changed = True changed = True
for job in self.data.get('job', {}).values(): for job in self.data.get("job", {}).values():
job = self._applyDefaults(job) job = self._applyDefaults(job)
job['name'] = self._getfullname(job) job["name"] = self._getfullname(job)
if jobs_glob and not matches(job['name'], jobs_glob): if jobs_glob and not matches(job["name"], jobs_glob):
logger.debug("Ignoring job {0}".format(job['name'])) logger.debug("Ignoring job {0}".format(job["name"]))
continue continue
logger.debug("Expanding job '{0}'".format(job['name'])) logger.debug("Expanding job '{0}'".format(job["name"]))
self._formatDescription(job) self._formatDescription(job)
self.jobs.append(job) self.jobs.append(job)
for view in self.data.get('view', {}).values(): for view in self.data.get("view", {}).values():
view['name'] = self._getfullname(view) view["name"] = self._getfullname(view)
if jobs_glob and not matches(view['name'], jobs_glob): if jobs_glob and not matches(view["name"], jobs_glob):
logger.debug("Ignoring view {0}".format(view['name'])) logger.debug("Ignoring view {0}".format(view["name"]))
continue continue
logger.debug("Expanding view '{0}'".format(view['name'])) logger.debug("Expanding view '{0}'".format(view["name"]))
self._formatDescription(view) self._formatDescription(view)
self.views.append(view) self.views.append(view)
for project in self.data.get('project', {}).values(): for project in self.data.get("project", {}).values():
logger.debug("Expanding project '{0}'".format(project['name'])) logger.debug("Expanding project '{0}'".format(project["name"]))
# use a set to check for duplicate job references in projects # use a set to check for duplicate job references in projects
seen = set() seen = set()
for jobspec in project.get('jobs', []): for jobspec in project.get("jobs", []):
if isinstance(jobspec, dict): if isinstance(jobspec, dict):
# Singleton dict containing dict of job-specific params # Singleton dict containing dict of job-specific params
jobname, jobparams = next(iter(jobspec.items())) jobname, jobparams = next(iter(jobspec.items()))
@ -275,18 +281,20 @@ class YamlParser(object):
if job: if job:
# Just naming an existing defined job # Just naming an existing defined job
if jobname in seen: if jobname in seen:
self._handle_dups("Duplicate job '{0}' specified " self._handle_dups(
"for project '{1}'" "Duplicate job '{0}' specified "
.format(jobname, project['name'])) "for project '{1}'".format(jobname, project["name"])
)
seen.add(jobname) seen.add(jobname)
continue continue
# see if it's a job group # see if it's a job group
group = self._getJobGroup(jobname) group = self._getJobGroup(jobname)
if group: if group:
for group_jobspec in group['jobs']: for group_jobspec in group["jobs"]:
if isinstance(group_jobspec, dict): if isinstance(group_jobspec, dict):
group_jobname, group_jobparams = \ group_jobname, group_jobparams = next(
next(iter(group_jobspec.items())) iter(group_jobspec.items())
)
if not isinstance(group_jobparams, dict): if not isinstance(group_jobparams, dict):
group_jobparams = {} group_jobparams = {}
else: else:
@ -297,8 +305,10 @@ class YamlParser(object):
if group_jobname in seen: if group_jobname in seen:
self._handle_dups( self._handle_dups(
"Duplicate job '{0}' specified for " "Duplicate job '{0}' specified for "
"project '{1}'".format(group_jobname, "project '{1}'".format(
project['name'])) group_jobname, project["name"]
)
)
seen.add(group_jobname) seen.add(group_jobname)
continue continue
template = self._getJobTemplate(group_jobname) template = self._getJobTemplate(group_jobname)
@ -308,10 +318,9 @@ class YamlParser(object):
d.update(group) d.update(group)
d.update(group_jobparams) d.update(group_jobparams)
# Except name, since the group's name is not useful # Except name, since the group's name is not useful
d['name'] = project['name'] d["name"] = project["name"]
if template: if template:
self._expandYamlForTemplateJob(d, template, self._expandYamlForTemplateJob(d, template, jobs_glob)
jobs_glob)
continue continue
# see if it's a template # see if it's a template
template = self._getJobTemplate(jobname) template = self._getJobTemplate(jobname)
@ -320,11 +329,12 @@ class YamlParser(object):
d.update(jobparams) d.update(jobparams)
self._expandYamlForTemplateJob(d, template, jobs_glob) self._expandYamlForTemplateJob(d, template, jobs_glob)
else: else:
raise JenkinsJobsException("Failed to find suitable " raise JenkinsJobsException(
"template named '{0}'" "Failed to find suitable "
.format(jobname)) "template named '{0}'".format(jobname)
)
for viewspec in project.get('views', []): for viewspec in project.get("views", []):
if isinstance(viewspec, dict): if isinstance(viewspec, dict):
# Singleton dict containing dict of view-specific params # Singleton dict containing dict of view-specific params
viewname, viewparams = next(iter(viewspec.items())) viewname, viewparams = next(iter(viewspec.items()))
@ -337,18 +347,20 @@ class YamlParser(object):
if view: if view:
# Just naming an existing defined view # Just naming an existing defined view
if viewname in seen: if viewname in seen:
self._handle_dups("Duplicate view '{0}' specified " self._handle_dups(
"for project '{1}'" "Duplicate view '{0}' specified "
.format(viewname, project['name'])) "for project '{1}'".format(viewname, project["name"])
)
seen.add(viewname) seen.add(viewname)
continue continue
# see if it's a view group # see if it's a view group
group = self._getViewGroup(viewname) group = self._getViewGroup(viewname)
if group: if group:
for group_viewspec in group['views']: for group_viewspec in group["views"]:
if isinstance(group_viewspec, dict): if isinstance(group_viewspec, dict):
group_viewname, group_viewparams = \ group_viewname, group_viewparams = next(
next(iter(group_viewspec.items())) iter(group_viewspec.items())
)
if not isinstance(group_viewparams, dict): if not isinstance(group_viewparams, dict):
group_viewparams = {} group_viewparams = {}
else: else:
@ -359,8 +371,10 @@ class YamlParser(object):
if group_viewname in seen: if group_viewname in seen:
self._handle_dups( self._handle_dups(
"Duplicate view '{0}' specified for " "Duplicate view '{0}' specified for "
"project '{1}'".format(group_viewname, "project '{1}'".format(
project['name'])) group_viewname, project["name"]
)
)
seen.add(group_viewname) seen.add(group_viewname)
continue continue
template = self._getViewTemplate(group_viewname) template = self._getViewTemplate(group_viewname)
@ -370,10 +384,9 @@ class YamlParser(object):
d.update(group) d.update(group)
d.update(group_viewparams) d.update(group_viewparams)
# Except name, since the group's name is not useful # Except name, since the group's name is not useful
d['name'] = project['name'] d["name"] = project["name"]
if template: if template:
self._expandYamlForTemplateView( self._expandYamlForTemplateView(d, template, jobs_glob)
d, template, jobs_glob)
continue continue
# see if it's a template # see if it's a template
template = self._getViewTemplate(viewname) template = self._getViewTemplate(viewname)
@ -382,41 +395,46 @@ class YamlParser(object):
d.update(viewparams) d.update(viewparams)
self._expandYamlForTemplateView(d, template, jobs_glob) self._expandYamlForTemplateView(d, template, jobs_glob)
else: else:
raise JenkinsJobsException("Failed to find suitable " raise JenkinsJobsException(
"template named '{0}'" "Failed to find suitable "
.format(viewname)) "template named '{0}'".format(viewname)
)
# check for duplicate generated jobs # check for duplicate generated jobs
seen = set() seen = set()
# walk the list in reverse so that last definition wins # walk the list in reverse so that last definition wins
for job in self.jobs[::-1]: for job in self.jobs[::-1]:
if job['name'] in seen: if job["name"] in seen:
self._handle_dups("Duplicate definitions for job '{0}' " self._handle_dups(
"specified".format(job['name'])) "Duplicate definitions for job '{0}' "
"specified".format(job["name"])
)
self.jobs.remove(job) self.jobs.remove(job)
seen.add(job['name']) seen.add(job["name"])
# check for duplicate generated views # check for duplicate generated views
seen_views = set() seen_views = set()
# walk the list in reverse so that last definition wins # walk the list in reverse so that last definition wins
for view in self.views[::-1]: for view in self.views[::-1]:
if view['name'] in seen_views: if view["name"] in seen_views:
self._handle_dups("Duplicate definitions for view '{0}' " self._handle_dups(
"specified".format(view['name'])) "Duplicate definitions for view '{0}' "
"specified".format(view["name"])
)
self.views.remove(view) self.views.remove(view)
seen_views.add(view['name']) seen_views.add(view["name"])
return self.jobs, self.views return self.jobs, self.views
def _expandYamlForTemplateJob(self, project, template, jobs_glob=None): def _expandYamlForTemplateJob(self, project, template, jobs_glob=None):
dimensions = [] dimensions = []
template_name = template['name'] template_name = template["name"]
# reject keys that are not useful during yaml expansion # reject keys that are not useful during yaml expansion
for k in ['jobs']: for k in ["jobs"]:
project.pop(k) project.pop(k)
excludes = project.pop('exclude', []) excludes = project.pop("exclude", [])
for (k, v) in project.items(): for (k, v) in project.items():
tmpk = '{{{0}}}'.format(k) tmpk = "{{{0}}}".format(k)
if tmpk not in template_name: if tmpk not in template_name:
continue continue
if type(v) == list: if type(v) == list:
@ -429,7 +447,7 @@ class YamlParser(object):
for values in itertools.product(*dimensions): for values in itertools.product(*dimensions):
params = copy.deepcopy(project) params = copy.deepcopy(project)
params = self._applyDefaults(params, template) params = self._applyDefaults(params, template)
params['template-name'] = re.sub(r'({|})', r'\1\1', template_name) params["template-name"] = re.sub(r"({|})", r"\1\1", template_name)
try: try:
expanded_values = {} expanded_values = {}
@ -441,29 +459,32 @@ class YamlParser(object):
else: else:
expanded_values[k] = v expanded_values[k] = v
except TypeError: except TypeError:
project_name = project.pop('name') project_name = project.pop("name")
logger.error( logger.error(
"Exception thrown while expanding template '%s' for " "Exception thrown while expanding template '%s' for "
"project '%s', with expansion arguments of:\n%s\n" "project '%s', with expansion arguments of:\n%s\n"
"Original project input variables for template:\n%s\n" "Original project input variables for template:\n%s\n"
"Most likely the inputs have items indented incorrectly " "Most likely the inputs have items indented incorrectly "
"to describe how they should be applied.\n\nNote yaml " "to describe how they should be applied.\n\nNote yaml "
"'null' is mapped to python's 'None'", template_name, "'null' is mapped to python's 'None'",
template_name,
project_name, project_name,
"".join(local_yaml.dump({k: v}, default_flow_style=False) "".join(
for (k, v) in values), local_yaml.dump({k: v}, default_flow_style=False)
local_yaml.dump(project, default_flow_style=False)) for (k, v) in values
),
local_yaml.dump(project, default_flow_style=False),
)
raise raise
params.update(expanded_values) params.update(expanded_values)
try: try:
params = deep_format(params, params) params = deep_format(params, params)
except Exception: except Exception:
logging.error( logging.error("Failure formatting params '%s' with itself", params)
"Failure formatting params '%s' with itself", params)
raise raise
if combination_matches(params, excludes): if combination_matches(params, excludes):
logger.debug('Excluding combination %s', str(params)) logger.debug("Excluding combination %s", str(params))
continue continue
for key in template.keys(): for key in template.keys():
@ -472,16 +493,22 @@ class YamlParser(object):
try: try:
expanded = deep_format( expanded = deep_format(
template, params, template,
self.jjb_config.yamlparser['allow_empty_variables']) params,
self.jjb_config.yamlparser["allow_empty_variables"],
)
except Exception: except Exception:
logging.error( logging.error(
"Failure formatting template '%s', containing '%s' with " "Failure formatting template '%s', containing '%s' with "
"params '%s'", template_name, template, params) "params '%s'",
template_name,
template,
params,
)
raise raise
expanded['name'] = self._getfullname(expanded) expanded["name"] = self._getfullname(expanded)
job_name = expanded.get('name') job_name = expanded.get("name")
if jobs_glob and not matches(job_name, jobs_glob): if jobs_glob and not matches(job_name, jobs_glob):
continue continue
@ -495,29 +522,29 @@ class YamlParser(object):
# Views related # Views related
def _getView(self, name): def _getView(self, name):
view = self.data.get('view', {}).get(name, None) view = self.data.get("view", {}).get(name, None)
if not view: if not view:
return view return view
return self._applyDefaults(view) return self._applyDefaults(view)
def _getViewGroup(self, name): def _getViewGroup(self, name):
return self.data.get('view-group', {}).get(name, None) return self.data.get("view-group", {}).get(name, None)
def _getViewTemplate(self, name): def _getViewTemplate(self, name):
view = self.data.get('view-template', {}).get(name, None) view = self.data.get("view-template", {}).get(name, None)
if not view: if not view:
return view return view
return self._applyDefaults(view) return self._applyDefaults(view)
def _expandYamlForTemplateView(self, project, template, views_glob=None): def _expandYamlForTemplateView(self, project, template, views_glob=None):
dimensions = [] dimensions = []
template_name = template['name'] template_name = template["name"]
# reject keys that are not useful during yaml expansion # reject keys that are not useful during yaml expansion
for k in ['views']: for k in ["views"]:
project.pop(k) project.pop(k)
excludes = project.pop('exclude', []) excludes = project.pop("exclude", [])
for (k, v) in project.items(): for (k, v) in project.items():
tmpk = '{{{0}}}'.format(k) tmpk = "{{{0}}}".format(k)
if tmpk not in template_name: if tmpk not in template_name:
continue continue
if type(v) == list: if type(v) == list:
@ -543,19 +570,19 @@ class YamlParser(object):
params.update(expanded_values) params.update(expanded_values)
params = deep_format(params, params) params = deep_format(params, params)
if combination_matches(params, excludes): if combination_matches(params, excludes):
logger.debug('Excluding combination %s', str(params)) logger.debug("Excluding combination %s", str(params))
continue continue
for key in template.keys(): for key in template.keys():
if key not in params: if key not in params:
params[key] = template[key] params[key] = template[key]
params['template-name'] = template_name params["template-name"] = template_name
expanded = deep_format( expanded = deep_format(
template, params, template, params, self.jjb_config.yamlparser["allow_empty_variables"]
self.jjb_config.yamlparser['allow_empty_variables']) )
view_name = expanded.get('name') view_name = expanded.get("name")
if views_glob and not matches(view_name, views_glob): if views_glob and not matches(view_name, views_glob):
continue continue

View File

@ -25,9 +25,7 @@ from jenkins_jobs.errors import JenkinsJobsException
from jenkins_jobs.formatter import deep_format from jenkins_jobs.formatter import deep_format
from jenkins_jobs.local_yaml import Jinja2Loader from jenkins_jobs.local_yaml import Jinja2Loader
__all__ = [ __all__ = ["ModuleRegistry"]
"ModuleRegistry"
]
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -47,12 +45,11 @@ class ModuleRegistry(object):
else: else:
self.plugins_dict = self._get_plugins_info_dict(plugins_list) self.plugins_dict = self._get_plugins_info_dict(plugins_list)
for entrypoint in pkg_resources.iter_entry_points( for entrypoint in pkg_resources.iter_entry_points(group="jenkins_jobs.modules"):
group='jenkins_jobs.modules'):
Mod = entrypoint.load() Mod = entrypoint.load()
mod = Mod(self) mod = Mod(self)
self.modules.append(mod) self.modules.append(mod)
self.modules.sort(key=operator.attrgetter('sequence')) self.modules.sort(key=operator.attrgetter("sequence"))
if mod.component_type is not None: if mod.component_type is not None:
self.modules_by_component_type[mod.component_type] = entrypoint self.modules_by_component_type[mod.component_type] = entrypoint
@ -64,12 +61,13 @@ class ModuleRegistry(object):
return a dictionary with the longName and shortName of the plugin return a dictionary with the longName and shortName of the plugin
mapped to its plugin info dictionary. mapped to its plugin info dictionary.
""" """
version = plugin_info.get('version', '0') version = plugin_info.get("version", "0")
plugin_info['version'] = re.sub(r'(.*)-(?:SNAPSHOT|BETA).*', plugin_info["version"] = re.sub(
r'\g<1>.preview', version) r"(.*)-(?:SNAPSHOT|BETA).*", r"\g<1>.preview", version
)
aliases = [] aliases = []
for key in ['longName', 'shortName']: for key in ["longName", "shortName"]:
value = plugin_info.get(key, None) value = plugin_info.get(key, None)
if value is not None: if value is not None:
aliases.append(value) aliases.append(value)
@ -130,8 +128,7 @@ class ModuleRegistry(object):
def set_parser_data(self, parser_data): def set_parser_data(self, parser_data):
self.__parser_data = parser_data self.__parser_data = parser_data
def dispatch(self, component_type, xml_parent, def dispatch(self, component_type, xml_parent, component, template_data={}):
component, template_data={}):
"""This is a method that you can call from your implementation of """This is a method that you can call from your implementation of
Base.gen_xml or component. It allows modules to define a type Base.gen_xml or component. It allows modules to define a type
of component, and benefit from extensibility via Python of component, and benefit from extensibility via Python
@ -152,8 +149,9 @@ class ModuleRegistry(object):
""" """
if component_type not in self.modules_by_component_type: if component_type not in self.modules_by_component_type:
raise JenkinsJobsException("Unknown component type: " raise JenkinsJobsException(
"'{0}'.".format(component_type)) "Unknown component type: " "'{0}'.".format(component_type)
)
entry_point = self.modules_by_component_type[component_type] entry_point = self.modules_by_component_type[component_type]
component_list_type = entry_point.load().component_list_type component_list_type = entry_point.load().component_list_type
@ -167,12 +165,16 @@ class ModuleRegistry(object):
# that don't contain any variables, we also deep format those. # that don't contain any variables, we also deep format those.
try: try:
component_data = deep_format( component_data = deep_format(
component_data, template_data, component_data,
self.jjb_config.yamlparser['allow_empty_variables']) template_data,
self.jjb_config.yamlparser["allow_empty_variables"],
)
except Exception: except Exception:
logging.error( logging.error(
"Failure formatting component ('%s') data '%s'", "Failure formatting component ('%s') data '%s'",
name, component_data) name,
component_data,
)
raise raise
else: else:
# The component is a simple string name, eg "run-tests" # The component is a simple string name, eg "run-tests"
@ -185,41 +187,54 @@ class ModuleRegistry(object):
module_eps = [] module_eps = []
# auto build entry points by inferring from base component_types # auto build entry points by inferring from base component_types
mod = pkg_resources.EntryPoint( mod = pkg_resources.EntryPoint(
"__all__", entry_point.module_name, dist=entry_point.dist) "__all__", entry_point.module_name, dist=entry_point.dist
)
Mod = mod.load() Mod = mod.load()
func_eps = [Mod.__dict__.get(a) for a in dir(Mod) func_eps = [
if isinstance(Mod.__dict__.get(a), Mod.__dict__.get(a)
types.FunctionType)] for a in dir(Mod)
if isinstance(Mod.__dict__.get(a), types.FunctionType)
]
for func_ep in func_eps: for func_ep in func_eps:
try: try:
# extract entry point based on docstring # extract entry point based on docstring
name_line = func_ep.__doc__.split('\n') name_line = func_ep.__doc__.split("\n")
if not name_line[0].startswith('yaml:'): if not name_line[0].startswith("yaml:"):
logger.debug("Ignoring '%s' as an entry point" % logger.debug("Ignoring '%s' as an entry point" % name_line)
name_line)
continue continue
ep_name = name_line[0].split(' ')[1] ep_name = name_line[0].split(" ")[1]
except (AttributeError, IndexError): except (AttributeError, IndexError):
# AttributeError by docstring not being defined as # AttributeError by docstring not being defined as
# a string to have split called on it. # a string to have split called on it.
# IndexError raised by name_line not containing anything # IndexError raised by name_line not containing anything
# after the 'yaml:' string. # after the 'yaml:' string.
logger.debug("Not including func '%s' as an entry point" logger.debug(
% func_ep.__name__) "Not including func '%s' as an entry point" % func_ep.__name__
)
continue continue
module_eps.append( module_eps.append(
pkg_resources.EntryPoint( pkg_resources.EntryPoint(
ep_name, entry_point.module_name, ep_name,
dist=entry_point.dist, attrs=(func_ep.__name__,))) entry_point.module_name,
dist=entry_point.dist,
attrs=(func_ep.__name__,),
)
)
logger.debug( logger.debug(
"Adding auto EP '%s=%s:%s'" % "Adding auto EP '%s=%s:%s'"
(ep_name, entry_point.module_name, func_ep.__name__)) % (ep_name, entry_point.module_name, func_ep.__name__)
)
# load from explicitly defined entry points # load from explicitly defined entry points
module_eps.extend(list(pkg_resources.iter_entry_points( module_eps.extend(
group='jenkins_jobs.{0}'.format(component_list_type)))) list(
pkg_resources.iter_entry_points(
group="jenkins_jobs.{0}".format(component_list_type)
)
)
)
eps = {} eps = {}
for module_ep in module_eps: for module_ep in module_eps:
@ -227,14 +242,14 @@ class ModuleRegistry(object):
raise JenkinsJobsException( raise JenkinsJobsException(
"Duplicate entry point found for component type: " "Duplicate entry point found for component type: "
"'{0}', '{0}'," "'{0}', '{0}',"
"name: '{1}'".format(component_type, name)) "name: '{1}'".format(component_type, name)
)
eps[module_ep.name] = module_ep eps[module_ep.name] = module_ep
# cache both sets of entry points # cache both sets of entry points
self._entry_points_cache[component_list_type] = eps self._entry_points_cache[component_list_type] = eps
logger.debug("Cached entry point group %s = %s", logger.debug("Cached entry point group %s = %s", component_list_type, eps)
component_list_type, eps)
# check for macro first # check for macro first
component = self.parser_data.get(component_type, {}).get(name) component = self.parser_data.get(component_type, {}).get(name)
@ -244,7 +259,8 @@ class ModuleRegistry(object):
logger.warning( logger.warning(
"You have a macro ('%s') defined for '%s' " "You have a macro ('%s') defined for '%s' "
"component type that is masking an inbuilt " "component type that is masking an inbuilt "
"definition" % (name, component_type)) "definition" % (name, component_type)
)
for b in component[component_list_type]: for b in component[component_list_type]:
# Pass component_data in as template data to this function # Pass component_data in as template data to this function
@ -255,6 +271,7 @@ class ModuleRegistry(object):
func = eps[name].load() func = eps[name].load()
func(self, xml_parent, component_data) func(self, xml_parent, component_data)
else: else:
raise JenkinsJobsException("Unknown entry point or macro '{0}' " raise JenkinsJobsException(
"for component type: '{1}'.". "Unknown entry point or macro '{0}' "
format(name, component_type)) "for component type: '{1}'.".format(name, component_type)
)

View File

@ -29,7 +29,7 @@ from sphinx.ext.autodoc import FunctionDocumenter
from sphinx.locale import _ from sphinx.locale import _
yaml_sig_re = re.compile(r'yaml:\s*(.*)') yaml_sig_re = re.compile(r"yaml:\s*(.*)")
class PyYAMLFunction(PyModulelevel): class PyYAMLFunction(PyModulelevel):
@ -48,15 +48,14 @@ class PyYAMLFunction(PyModulelevel):
retann = None retann = None
# determine module and class name (if applicable), as well as full name # determine module and class name (if applicable), as well as full name
modname = self.options.get( modname = self.options.get("module", self.env.temp_data.get("py:module"))
'module', self.env.temp_data.get('py:module')) classname = self.env.temp_data.get("py:class")
classname = self.env.temp_data.get('py:class')
fullname = name fullname = name
signode['module'] = modname signode["module"] = modname
signode['class'] = classname signode["class"] = classname
signode['fullname'] = fullname signode["fullname"] = fullname
sig_prefix = self.get_signature_prefix(sig) sig_prefix = self.get_signature_prefix(sig)
if sig_prefix: if sig_prefix:
@ -65,7 +64,7 @@ class PyYAMLFunction(PyModulelevel):
if name_prefix: if name_prefix:
signode += addnodes.desc_addname(name_prefix, name_prefix) signode += addnodes.desc_addname(name_prefix, name_prefix)
anno = self.options.get('annotation') anno = self.options.get("annotation")
signode += addnodes.desc_name(name, name) signode += addnodes.desc_name(name, name)
if not arglist: if not arglist:
@ -75,29 +74,30 @@ class PyYAMLFunction(PyModulelevel):
if retann: if retann:
signode += addnodes.desc_returns(retann, retann) signode += addnodes.desc_returns(retann, retann)
if anno: if anno:
signode += addnodes.desc_annotation(' ' + anno, ' ' + anno) signode += addnodes.desc_annotation(" " + anno, " " + anno)
return fullname, name_prefix return fullname, name_prefix
_pseudo_parse_arglist(signode, arglist) _pseudo_parse_arglist(signode, arglist)
if retann: if retann:
signode += addnodes.desc_returns(retann, retann) signode += addnodes.desc_returns(retann, retann)
if anno: if anno:
signode += addnodes.desc_annotation(' ' + anno, ' ' + anno) signode += addnodes.desc_annotation(" " + anno, " " + anno)
return fullname, name_prefix return fullname, name_prefix
def get_index_text(self, modname, name_cls): def get_index_text(self, modname, name_cls):
return _('%s (in module %s)') % (name_cls[0], modname) return _("%s (in module %s)") % (name_cls[0], modname)
class YAMLFunctionDocumenter(FunctionDocumenter): class YAMLFunctionDocumenter(FunctionDocumenter):
priority = FunctionDocumenter.priority + 10 priority = FunctionDocumenter.priority + 10
objtype = 'yamlfunction' objtype = "yamlfunction"
directivetype = 'yamlfunction' directivetype = "yamlfunction"
@classmethod @classmethod
def can_document_member(cls, member, membername, isattr, parent): def can_document_member(cls, member, membername, isattr, parent):
if not FunctionDocumenter.can_document_member(member, membername, if not FunctionDocumenter.can_document_member(
isattr, parent): member, membername, isattr, parent
):
return False return False
if member.__doc__ is not None and yaml_sig_re.match(member.__doc__): if member.__doc__ is not None and yaml_sig_re.match(member.__doc__):
return True return True
@ -108,7 +108,7 @@ class YAMLFunctionDocumenter(FunctionDocumenter):
if len(docstrings) != 1: if len(docstrings) != 1:
return return
doclines = docstrings[0] doclines = docstrings[0]
setattr(self, '__new_doclines', doclines) setattr(self, "__new_doclines", doclines)
if not doclines: if not doclines:
return return
# match first line of docstring against signature RE # match first line of docstring against signature RE
@ -121,11 +121,11 @@ class YAMLFunctionDocumenter(FunctionDocumenter):
i = 1 i = 1
while i < len(doclines) and not doclines[i].strip(): while i < len(doclines) and not doclines[i].strip():
i += 1 i += 1
setattr(self, '__new_doclines', doclines[i:]) setattr(self, "__new_doclines", doclines[i:])
return name return name
def get_doc(self, encoding=None, ignore=1): def get_doc(self, encoding=None, ignore=1):
lines = getattr(self, '__new_doclines', None) lines = getattr(self, "__new_doclines", None)
if lines is not None: if lines is not None:
return [lines] return [lines]
return Documenter.get_doc(self, encoding, ignore) return Documenter.get_doc(self, encoding, ignore)
@ -133,7 +133,7 @@ class YAMLFunctionDocumenter(FunctionDocumenter):
def format_signature(self): def format_signature(self):
result = self._find_signature() result = self._find_signature()
self._name = result self._name = result
return '' return ""
def format_name(self): def format_name(self):
return self._name return self._name
@ -141,4 +141,4 @@ class YAMLFunctionDocumenter(FunctionDocumenter):
def setup(app): def setup(app):
app.add_autodocumenter(YAMLFunctionDocumenter) app.add_autodocumenter(YAMLFunctionDocumenter)
app.add_directive_to_domain('py', 'yamlfunction', PyYAMLFunction) app.add_directive_to_domain("py", "yamlfunction", PyYAMLFunction)

View File

@ -22,14 +22,14 @@ import os.path
from six.moves import input from six.moves import input
def wrap_stream(stream, encoding='utf-8'): def wrap_stream(stream, encoding="utf-8"):
try: try:
stream_enc = stream.encoding stream_enc = stream.encoding
except AttributeError: except AttributeError:
stream_enc = locale.getpreferredencoding() stream_enc = locale.getpreferredencoding()
if hasattr(stream, 'buffer'): if hasattr(stream, "buffer"):
stream = stream.buffer stream = stream.buffer
if str(stream_enc).lower() == str(encoding).lower(): if str(stream_enc).lower() == str(encoding).lower():
@ -47,21 +47,27 @@ def recurse_path(root, excludes=None):
patterns = [e for e in excludes if os.path.sep not in e] patterns = [e for e in excludes if os.path.sep not in e]
absolute = [e for e in excludes if os.path.isabs(e)] absolute = [e for e in excludes if os.path.isabs(e)]
relative = [e for e in excludes if os.path.sep in e and relative = [e for e in excludes if os.path.sep in e and not os.path.isabs(e)]
not os.path.isabs(e)]
for root, dirs, files in os.walk(basepath, topdown=True): for root, dirs, files in os.walk(basepath, topdown=True):
# sort in-place to ensure dirnames are visited in alphabetical order # sort in-place to ensure dirnames are visited in alphabetical order
# a predictable order makes it easier to use the retain_anchors option # a predictable order makes it easier to use the retain_anchors option
dirs.sort() dirs.sort()
dirs[:] = [ dirs[:] = [
d for d in dirs d
for d in dirs
if not any([fnmatch.fnmatch(d, pattern) for pattern in patterns]) if not any([fnmatch.fnmatch(d, pattern) for pattern in patterns])
if not any([fnmatch.fnmatch(os.path.abspath(os.path.join(root, d)), if not any(
path) [
for path in absolute]) fnmatch.fnmatch(os.path.abspath(os.path.join(root, d)), path)
if not any([fnmatch.fnmatch(os.path.relpath(os.path.join(root, d)), for path in absolute
path) ]
for path in relative]) )
if not any(
[
fnmatch.fnmatch(os.path.relpath(os.path.join(root, d)), path)
for path in relative
]
)
] ]
pathlist.extend([os.path.join(root, path) for path in dirs]) pathlist.extend([os.path.join(root, path) for path in dirs])
@ -69,5 +75,5 @@ def recurse_path(root, excludes=None):
def confirm(question): def confirm(question):
answer = input('%s (Y/N): ' % question).upper().strip() answer = input("%s (Y/N): " % question).upper().strip()
return answer == 'Y' return answer == "Y"

View File

@ -17,4 +17,4 @@
from pbr.version import VersionInfo from pbr.version import VersionInfo
version_info = VersionInfo('jenkins-job-builder') version_info = VersionInfo("jenkins-job-builder")

View File

@ -22,10 +22,7 @@ import xml.etree.ElementTree as XML
from jenkins_jobs import errors from jenkins_jobs import errors
__all__ = [ __all__ = ["XmlJobGenerator", "XmlJob"]
"XmlJobGenerator",
"XmlJob"
]
def remove_ignorable_whitespace(node): def remove_ignorable_whitespace(node):
@ -59,8 +56,8 @@ class XmlJob(object):
return hashlib.md5(self.output()).hexdigest() return hashlib.md5(self.output()).hexdigest()
def output(self): def output(self):
out = minidom.parseString(XML.tostring(self.xml, encoding='UTF-8')) out = minidom.parseString(XML.tostring(self.xml, encoding="UTF-8"))
return out.toprettyxml(indent=' ', encoding='utf-8') return out.toprettyxml(indent=" ", encoding="utf-8")
class XmlGenerator(object): class XmlGenerator(object):
@ -86,25 +83,29 @@ class XmlGenerator(object):
kind = data.get(self.kind_attribute, self.kind_default) kind = data.get(self.kind_attribute, self.kind_default)
for ep in pkg_resources.iter_entry_points( for ep in pkg_resources.iter_entry_points(
group=self.entry_point_group, name=kind): group=self.entry_point_group, name=kind
):
Mod = ep.load() Mod = ep.load()
mod = Mod(self.registry) mod = Mod(self.registry)
xml = mod.root_xml(data) xml = mod.root_xml(data)
if "view-type" not in data: if "view-type" not in data:
self._gen_xml(xml, data) self._gen_xml(xml, data)
obj = XmlJob(xml, data['name']) obj = XmlJob(xml, data["name"])
return obj return obj
names = [ names = [
ep.name for ep in pkg_resources.iter_entry_points( ep.name
group=self.entry_point_group)] for ep in pkg_resources.iter_entry_points(group=self.entry_point_group)
]
raise errors.JenkinsJobsException( raise errors.JenkinsJobsException(
'Unrecognized {}: {} (supported types are: {})'.format( "Unrecognized {}: {} (supported types are: {})".format(
self.kind_attribute, kind, ', '.join(names))) self.kind_attribute, kind, ", ".join(names)
)
)
def _gen_xml(self, xml, data): def _gen_xml(self, xml, data):
for module in self.registry.modules: for module in self.registry.modules:
if hasattr(module, 'gen_xml'): if hasattr(module, "gen_xml"):
module.gen_xml(xml, data) module.gen_xml(xml, data)
@ -112,15 +113,17 @@ class XmlJobGenerator(XmlGenerator):
""" This class is responsible for generating Jenkins Configuration XML from """ This class is responsible for generating Jenkins Configuration XML from
a compatible intermediate representation of Jenkins Jobs. a compatible intermediate representation of Jenkins Jobs.
""" """
entry_point_group = 'jenkins_jobs.projects'
kind_attribute = 'project-type' entry_point_group = "jenkins_jobs.projects"
kind_default = 'freestyle' kind_attribute = "project-type"
kind_default = "freestyle"
class XmlViewGenerator(XmlGenerator): class XmlViewGenerator(XmlGenerator):
""" This class is responsible for generating Jenkins Configuration XML from """ This class is responsible for generating Jenkins Configuration XML from
a compatible intermediate representation of Jenkins Views. a compatible intermediate representation of Jenkins Views.
""" """
entry_point_group = 'jenkins_jobs.views'
kind_attribute = 'view-type' entry_point_group = "jenkins_jobs.views"
kind_default = 'list' kind_attribute = "view-type"
kind_default = "list"

View File

@ -100,10 +100,8 @@ jenkins_jobs.modules =
zuul=jenkins_jobs.modules.zuul:Zuul zuul=jenkins_jobs.modules.zuul:Zuul
[flake8] [flake8]
# These are ignored intentionally in openstack-infra projects; please # Based on https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/
# don't submit patches that solely correct them or enable them. ignore = E125,E128,E203,E501,H,W504,W503
# W504 is controversial an apparently conflicting with W503, being impossible
# to solve both of them while still keeping the line length limited.
ignore = E125,E128,H,W504
show-source = True show-source = True
exclude = .virtualenv,.venv,.tox,dist,build,*.egg,.test exclude = .virtualenv,.venv,.tox,dist,build,*.egg,.test
max-line-length = 88

View File

@ -24,6 +24,4 @@ try:
except ImportError: except ImportError:
pass pass
setuptools.setup( setuptools.setup(setup_requires=["pbr>=1.8"], pbr=True)
setup_requires=['pbr>=1.8'],
pbr=True)

View File

@ -59,9 +59,13 @@ except ImportError:
import mock # noqa import mock # noqa
def get_scenarios(fixtures_path, in_ext='yaml', out_ext='xml', def get_scenarios(
plugins_info_ext='plugins_info.yaml', fixtures_path,
filter_func=None): in_ext="yaml",
out_ext="xml",
plugins_info_ext="plugins_info.yaml",
filter_func=None,
):
"""Returns a list of scenarios, each scenario being described """Returns a list of scenarios, each scenario being described
by two parameters (yaml and xml filenames by default). by two parameters (yaml and xml filenames by default).
- content of the fixture output file (aka expected) - content of the fixture output file (aka expected)
@ -75,8 +79,9 @@ def get_scenarios(fixtures_path, in_ext='yaml', out_ext='xml',
else: else:
files[fn] = [os.path.join(dirpath, fn)] files[fn] = [os.path.join(dirpath, fn)]
input_files = [files[f][0] for f in files if input_files = [
re.match(r'.*\.{0}$'.format(in_ext), f)] files[f][0] for f in files if re.match(r".*\.{0}$".format(in_ext), f)
]
for input_filename in input_files: for input_filename in input_files:
if input_filename.endswith(plugins_info_ext): if input_filename.endswith(plugins_info_ext):
@ -85,21 +90,22 @@ def get_scenarios(fixtures_path, in_ext='yaml', out_ext='xml',
if callable(filter_func) and filter_func(input_filename): if callable(filter_func) and filter_func(input_filename):
continue continue
output_candidate = re.sub(r'\.{0}$'.format(in_ext), output_candidate = re.sub(
'.{0}'.format(out_ext), input_filename) r"\.{0}$".format(in_ext), ".{0}".format(out_ext), input_filename
)
# assume empty file if no output candidate found # assume empty file if no output candidate found
if os.path.basename(output_candidate) in files: if os.path.basename(output_candidate) in files:
out_filenames = files[os.path.basename(output_candidate)] out_filenames = files[os.path.basename(output_candidate)]
else: else:
out_filenames = None out_filenames = None
plugins_info_candidate = re.sub(r'\.{0}$'.format(in_ext), plugins_info_candidate = re.sub(
'.{0}'.format(plugins_info_ext), r"\.{0}$".format(in_ext), ".{0}".format(plugins_info_ext), input_filename
input_filename) )
if os.path.basename(plugins_info_candidate) not in files: if os.path.basename(plugins_info_candidate) not in files:
plugins_info_candidate = None plugins_info_candidate = None
conf_candidate = re.sub(r'\.yaml$|\.json$', '.conf', input_filename) conf_candidate = re.sub(r"\.yaml$|\.json$", ".conf", input_filename)
conf_filename = files.get(os.path.basename(conf_candidate), None) conf_filename = files.get(os.path.basename(conf_candidate), None)
if conf_filename: if conf_filename:
@ -108,12 +114,17 @@ def get_scenarios(fixtures_path, in_ext='yaml', out_ext='xml',
# for testing purposes we want to avoid using user config files # for testing purposes we want to avoid using user config files
conf_filename = os.devnull conf_filename = os.devnull
scenarios.append((input_filename, { scenarios.append(
'in_filename': input_filename, (
'out_filenames': out_filenames, input_filename,
'conf_filename': conf_filename, {
'plugins_info_filename': plugins_info_candidate, "in_filename": input_filename,
})) "out_filenames": out_filenames,
"conf_filename": conf_filename,
"plugins_info_filename": plugins_info_candidate,
},
)
)
return scenarios return scenarios
@ -121,7 +132,7 @@ def get_scenarios(fixtures_path, in_ext='yaml', out_ext='xml',
class BaseTestCase(testtools.TestCase): class BaseTestCase(testtools.TestCase):
# TestCase settings: # TestCase settings:
maxDiff = None # always dump text difference maxDiff = None # always dump text difference
longMessage = True # keep normal error message when providing our longMessage = True # keep normal error message when providing our
def setUp(self): def setUp(self):
@ -137,12 +148,12 @@ class BaseTestCase(testtools.TestCase):
# Read XML content, assuming it is unicode encoded # Read XML content, assuming it is unicode encoded
xml_content = "" xml_content = ""
for f in sorted(self.out_filenames): for f in sorted(self.out_filenames):
with io.open(f, 'r', encoding='utf-8') as xml_file: with io.open(f, "r", encoding="utf-8") as xml_file:
xml_content += u"%s" % xml_file.read() xml_content += u"%s" % xml_file.read()
return xml_content return xml_content
def _read_yaml_content(self, filename): def _read_yaml_content(self, filename):
with io.open(filename, 'r', encoding='utf-8') as yaml_file: with io.open(filename, "r", encoding="utf-8") as yaml_file:
yaml_content = yaml.load(yaml_file) yaml_content = yaml.load(yaml_file)
return yaml_content return yaml_content
@ -170,10 +181,10 @@ class BaseScenariosTestCase(testscenarios.TestWithScenarios, BaseTestCase):
plugins_info = None plugins_info = None
if self.plugins_info_filename: if self.plugins_info_filename:
plugins_info = self._read_yaml_content(self.plugins_info_filename) plugins_info = self._read_yaml_content(self.plugins_info_filename)
self.addDetail("plugins-info-filename", self.addDetail(
text_content(self.plugins_info_filename)) "plugins-info-filename", text_content(self.plugins_info_filename)
self.addDetail("plugins-info", )
text_content(str(plugins_info))) self.addDetail("plugins-info", text_content(str(plugins_info)))
parser = YamlParser(jjb_config) parser = YamlParser(jjb_config)
registry = ModuleRegistry(jjb_config, plugins_info) registry = ModuleRegistry(jjb_config, plugins_info)
@ -182,76 +193,89 @@ class BaseScenariosTestCase(testscenarios.TestWithScenarios, BaseTestCase):
pub = self.klass(registry) pub = self.klass(registry)
project = None project = None
if ('project-type' in yaml_content): if "project-type" in yaml_content:
if (yaml_content['project-type'] == "maven"): if yaml_content["project-type"] == "maven":
project = project_maven.Maven(registry) project = project_maven.Maven(registry)
elif (yaml_content['project-type'] == "matrix"): elif yaml_content["project-type"] == "matrix":
project = project_matrix.Matrix(registry) project = project_matrix.Matrix(registry)
elif (yaml_content['project-type'] == "flow"): elif yaml_content["project-type"] == "flow":
project = project_flow.Flow(registry) project = project_flow.Flow(registry)
elif (yaml_content['project-type'] == "multijob"): elif yaml_content["project-type"] == "multijob":
project = project_multijob.MultiJob(registry) project = project_multijob.MultiJob(registry)
elif (yaml_content['project-type'] == "multibranch"): elif yaml_content["project-type"] == "multibranch":
project = project_multibranch.WorkflowMultiBranch(registry) project = project_multibranch.WorkflowMultiBranch(registry)
elif (yaml_content['project-type'] == "multibranch-defaults"): elif yaml_content["project-type"] == "multibranch-defaults":
project = project_multibranch.WorkflowMultiBranchDefaults(registry) # noqa project = project_multibranch.WorkflowMultiBranchDefaults(
elif (yaml_content['project-type'] == "externaljob"): registry
) # noqa
elif yaml_content["project-type"] == "externaljob":
project = project_externaljob.ExternalJob(registry) project = project_externaljob.ExternalJob(registry)
if 'view-type' in yaml_content: if "view-type" in yaml_content:
if yaml_content['view-type'] == "all": if yaml_content["view-type"] == "all":
project = view_all.All(None) project = view_all.All(None)
elif yaml_content['view-type'] == "list": elif yaml_content["view-type"] == "list":
project = view_list.List(None) project = view_list.List(None)
elif yaml_content['view-type'] == "pipeline": elif yaml_content["view-type"] == "pipeline":
project = view_pipeline.Pipeline(None) project = view_pipeline.Pipeline(None)
else: else:
raise InvalidAttributeError( raise InvalidAttributeError("view-type", yaml_content["view-type"])
'view-type', yaml_content['view-type'])
if project: if project:
xml_project = project.root_xml(yaml_content) xml_project = project.root_xml(yaml_content)
else: else:
xml_project = XML.Element('project') xml_project = XML.Element("project")
# Generate the XML tree directly with modules/general # Generate the XML tree directly with modules/general
pub.gen_xml(xml_project, yaml_content) pub.gen_xml(xml_project, yaml_content)
# check output file is under correct path # check output file is under correct path
if 'name' in yaml_content: if "name" in yaml_content:
prefix = os.path.dirname(self.in_filename) prefix = os.path.dirname(self.in_filename)
# split using '/' since fullname uses URL path separator # split using '/' since fullname uses URL path separator
expected_folders = [os.path.normpath( expected_folders = [
os.path.join(prefix, os.path.normpath(
'/'.join(parser._getfullname(yaml_content). os.path.join(
split('/')[:-1])))] prefix,
"/".join(parser._getfullname(yaml_content).split("/")[:-1]),
)
)
]
actual_folders = [os.path.dirname(f) for f in self.out_filenames] actual_folders = [os.path.dirname(f) for f in self.out_filenames]
self.assertEquals( self.assertEquals(
expected_folders, actual_folders, expected_folders,
"Output file under wrong path, was '%s', should be '%s'" % actual_folders,
(self.out_filenames[0], "Output file under wrong path, was '%s', should be '%s'"
os.path.join(expected_folders[0], % (
os.path.basename(self.out_filenames[0])))) self.out_filenames[0],
os.path.join(
expected_folders[0], os.path.basename(self.out_filenames[0])
),
),
)
# Prettify generated XML # Prettify generated XML
pretty_xml = XmlJob(xml_project, 'fixturejob').output().decode('utf-8') pretty_xml = XmlJob(xml_project, "fixturejob").output().decode("utf-8")
self.assertThat( self.assertThat(
pretty_xml, pretty_xml,
testtools.matchers.DocTestMatches(expected_xml, testtools.matchers.DocTestMatches(
doctest.ELLIPSIS | expected_xml, doctest.ELLIPSIS | doctest.REPORT_NDIFF
doctest.REPORT_NDIFF) ),
) )
class SingleJobTestCase(BaseScenariosTestCase): class SingleJobTestCase(BaseScenariosTestCase):
def test_yaml_snippet(self): def test_yaml_snippet(self):
config = self._get_config() config = self._get_config()
expected_xml = self._read_utf8_content().strip() \ expected_xml = (
.replace('<BLANKLINE>', '').replace('\n\n', '\n') self._read_utf8_content()
.strip()
.replace("<BLANKLINE>", "")
.replace("\n\n", "\n")
)
parser = YamlParser(config) parser = YamlParser(config)
parser.parse(self.in_filename) parser.parse(self.in_filename)
@ -259,10 +283,10 @@ class SingleJobTestCase(BaseScenariosTestCase):
plugins_info = None plugins_info = None
if self.plugins_info_filename: if self.plugins_info_filename:
plugins_info = self._read_yaml_content(self.plugins_info_filename) plugins_info = self._read_yaml_content(self.plugins_info_filename)
self.addDetail("plugins-info-filename", self.addDetail(
text_content(self.plugins_info_filename)) "plugins-info-filename", text_content(self.plugins_info_filename)
self.addDetail("plugins-info", )
text_content(str(plugins_info))) self.addDetail("plugins-info", text_content(str(plugins_info)))
registry = ModuleRegistry(config, plugins_info) registry = ModuleRegistry(config, plugins_info)
registry.set_parser_data(parser.data) registry.set_parser_data(parser.data)
@ -277,53 +301,62 @@ class SingleJobTestCase(BaseScenariosTestCase):
# check reference files are under correct path for folders # check reference files are under correct path for folders
prefix = os.path.dirname(self.in_filename) prefix = os.path.dirname(self.in_filename)
# split using '/' since fullname uses URL path separator # split using '/' since fullname uses URL path separator
expected_folders = list(set([ expected_folders = list(
os.path.normpath( set(
os.path.join(prefix, [
'/'.join(job_data['name'].split('/')[:-1]))) os.path.normpath(
for job_data in job_data_list os.path.join(prefix, "/".join(job_data["name"].split("/")[:-1]))
])) )
for job_data in job_data_list
]
)
)
actual_folders = [os.path.dirname(f) for f in self.out_filenames] actual_folders = [os.path.dirname(f) for f in self.out_filenames]
six.assertCountEqual( six.assertCountEqual(
self, self,
expected_folders, actual_folders, expected_folders,
"Output file under wrong path, was '%s', should be '%s'" % actual_folders,
(self.out_filenames[0], "Output file under wrong path, was '%s', should be '%s'"
os.path.join(expected_folders[0], % (
os.path.basename(self.out_filenames[0])))) self.out_filenames[0],
os.path.join(
expected_folders[0], os.path.basename(self.out_filenames[0])
),
),
)
# Prettify generated XML # Prettify generated XML
pretty_xml = u"\n".join(job.output().decode('utf-8') pretty_xml = (
for job in xml_jobs) \ u"\n".join(job.output().decode("utf-8") for job in xml_jobs)
.strip().replace('\n\n', '\n') .strip()
.replace("\n\n", "\n")
)
self.assertThat( self.assertThat(
pretty_xml, pretty_xml,
testtools.matchers.DocTestMatches(expected_xml, testtools.matchers.DocTestMatches(
doctest.ELLIPSIS | expected_xml, doctest.ELLIPSIS | doctest.REPORT_NDIFF
doctest.REPORT_NDIFF)) ),
)
class JsonTestCase(BaseScenariosTestCase): class JsonTestCase(BaseScenariosTestCase):
def test_yaml_snippet(self): def test_yaml_snippet(self):
expected_json = self._read_utf8_content() expected_json = self._read_utf8_content()
yaml_content = self._read_yaml_content(self.in_filename) yaml_content = self._read_yaml_content(self.in_filename)
pretty_json = json.dumps(yaml_content, indent=4, pretty_json = json.dumps(yaml_content, indent=4, separators=(",", ": "))
separators=(',', ': '))
self.assertThat( self.assertThat(
pretty_json, pretty_json,
testtools.matchers.DocTestMatches(expected_json, testtools.matchers.DocTestMatches(
doctest.ELLIPSIS | expected_json, doctest.ELLIPSIS | doctest.REPORT_NDIFF
doctest.REPORT_NDIFF) ),
) )
class YamlTestCase(BaseScenariosTestCase): class YamlTestCase(BaseScenariosTestCase):
def test_yaml_snippet(self): def test_yaml_snippet(self):
expected_yaml = self._read_utf8_content() expected_yaml = self._read_utf8_content()
yaml_content = self._read_yaml_content(self.in_filename) yaml_content = self._read_yaml_content(self.in_filename)
@ -337,7 +370,7 @@ class YamlTestCase(BaseScenariosTestCase):
self.assertThat( self.assertThat(
pretty_yaml, pretty_yaml,
testtools.matchers.DocTestMatches(expected_yaml, testtools.matchers.DocTestMatches(
doctest.ELLIPSIS | expected_yaml, doctest.ELLIPSIS | doctest.REPORT_NDIFF
doctest.REPORT_NDIFF) ),
) )

View File

@ -22,6 +22,6 @@ from tests import base
class TestCaseModuleBuilders(base.BaseScenariosTestCase): class TestCaseModuleBuilders(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = builders.Builders klass = builders.Builders

View File

@ -21,28 +21,25 @@ from tests.base import mock
class TestCaseJobCache(base.BaseTestCase): class TestCaseJobCache(base.BaseTestCase):
@mock.patch("jenkins_jobs.builder.JobCache.get_cache_dir", lambda x: "/bad/file")
@mock.patch('jenkins_jobs.builder.JobCache.get_cache_dir',
lambda x: '/bad/file')
def test_save_on_exit(self): def test_save_on_exit(self):
""" """
Test that the cache is saved on normal object deletion Test that the cache is saved on normal object deletion
""" """
with mock.patch('jenkins_jobs.builder.JobCache.save') as save_mock: with mock.patch("jenkins_jobs.builder.JobCache.save") as save_mock:
with mock.patch('os.path.isfile', return_value=False): with mock.patch("os.path.isfile", return_value=False):
with mock.patch('jenkins_jobs.builder.JobCache._lock'): with mock.patch("jenkins_jobs.builder.JobCache._lock"):
jenkins_jobs.builder.JobCache("dummy") jenkins_jobs.builder.JobCache("dummy")
save_mock.assert_called_with() save_mock.assert_called_with()
@mock.patch('jenkins_jobs.builder.JobCache.get_cache_dir', @mock.patch("jenkins_jobs.builder.JobCache.get_cache_dir", lambda x: "/bad/file")
lambda x: '/bad/file')
def test_cache_file(self): def test_cache_file(self):
""" """
Test providing a cachefile. Test providing a cachefile.
""" """
test_file = os.path.abspath(__file__) test_file = os.path.abspath(__file__)
with mock.patch('os.path.join', return_value=test_file): with mock.patch("os.path.join", return_value=test_file):
with mock.patch('yaml.load'): with mock.patch("yaml.load"):
with mock.patch('jenkins_jobs.builder.JobCache._lock'): with mock.patch("jenkins_jobs.builder.JobCache._lock"):
jenkins_jobs.builder.JobCache("dummy").data = None jenkins_jobs.builder.JobCache("dummy").data = None

View File

@ -24,51 +24,49 @@ from tests.base import mock
from tests.cmd.test_cmd import CmdTestsBase from tests.cmd.test_cmd import CmdTestsBase
@mock.patch('jenkins_jobs.builder.JenkinsManager.get_plugins_info', @mock.patch("jenkins_jobs.builder.JenkinsManager.get_plugins_info", mock.MagicMock)
mock.MagicMock)
class DeleteTests(CmdTestsBase): class DeleteTests(CmdTestsBase):
@mock.patch("jenkins_jobs.cli.subcommand.update." "JenkinsManager.delete_jobs")
@mock.patch('jenkins_jobs.cli.subcommand.update.' @mock.patch("jenkins_jobs.cli.subcommand.update." "JenkinsManager.delete_views")
'JenkinsManager.delete_jobs')
@mock.patch('jenkins_jobs.cli.subcommand.update.'
'JenkinsManager.delete_views')
def test_delete_single_job(self, delete_job_mock, delete_view_mock): def test_delete_single_job(self, delete_job_mock, delete_view_mock):
""" """
Test handling the deletion of a single Jenkins job. Test handling the deletion of a single Jenkins job.
""" """
args = ['--conf', self.default_config_file, 'delete', 'test_job'] args = ["--conf", self.default_config_file, "delete", "test_job"]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
@mock.patch('jenkins_jobs.cli.subcommand.update.' @mock.patch("jenkins_jobs.cli.subcommand.update." "JenkinsManager.delete_jobs")
'JenkinsManager.delete_jobs') @mock.patch("jenkins_jobs.cli.subcommand.update." "JenkinsManager.delete_views")
@mock.patch('jenkins_jobs.cli.subcommand.update.'
'JenkinsManager.delete_views')
def test_delete_multiple_jobs(self, delete_job_mock, delete_view_mock): def test_delete_multiple_jobs(self, delete_job_mock, delete_view_mock):
""" """
Test handling the deletion of multiple Jenkins jobs. Test handling the deletion of multiple Jenkins jobs.
""" """
args = ['--conf', self.default_config_file, args = ["--conf", self.default_config_file, "delete", "test_job1", "test_job2"]
'delete', 'test_job1', 'test_job2']
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
@mock.patch('jenkins_jobs.builder.JenkinsManager.delete_job') @mock.patch("jenkins_jobs.builder.JenkinsManager.delete_job")
def test_delete_using_glob_params(self, delete_job_mock): def test_delete_using_glob_params(self, delete_job_mock):
""" """
Test handling the deletion of multiple Jenkins jobs using the glob Test handling the deletion of multiple Jenkins jobs using the glob
parameters feature. parameters feature.
""" """
args = ['--conf', self.default_config_file, args = [
'delete', '--path', "--conf",
os.path.join(self.fixtures_path, self.default_config_file,
'cmd-002.yaml'), "delete",
'*bar*'] "--path",
os.path.join(self.fixtures_path, "cmd-002.yaml"),
"*bar*",
]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
calls = [mock.call('bar001'), mock.call('bar002')] calls = [mock.call("bar001"), mock.call("bar002")]
delete_job_mock.assert_has_calls(calls, any_order=True) delete_job_mock.assert_has_calls(calls, any_order=True)
self.assertEqual(delete_job_mock.call_count, len(calls), self.assertEqual(
"Jenkins.delete_job() was called '%s' times when " delete_job_mock.call_count,
"expected '%s'" % (delete_job_mock.call_count, len(calls),
len(calls))) "Jenkins.delete_job() was called '%s' times when "
"expected '%s'" % (delete_job_mock.call_count, len(calls)),
)

View File

@ -21,31 +21,27 @@ from tests.base import mock
from tests.cmd.test_cmd import CmdTestsBase from tests.cmd.test_cmd import CmdTestsBase
@mock.patch('jenkins_jobs.builder.JenkinsManager.get_plugins_info', @mock.patch("jenkins_jobs.builder.JenkinsManager.get_plugins_info", mock.MagicMock)
mock.MagicMock)
class DeleteAllTests(CmdTestsBase): class DeleteAllTests(CmdTestsBase):
@mock.patch("jenkins_jobs.cli.subcommand.update." "JenkinsManager.delete_all_jobs")
@mock.patch('jenkins_jobs.cli.subcommand.update.'
'JenkinsManager.delete_all_jobs')
def test_delete_all_accept(self, delete_job_mock): def test_delete_all_accept(self, delete_job_mock):
""" """
Test handling the deletion of a single Jenkins job. Test handling the deletion of a single Jenkins job.
""" """
args = ['--conf', self.default_config_file, 'delete-all'] args = ["--conf", self.default_config_file, "delete-all"]
with mock.patch('jenkins_jobs.builder.JenkinsManager.get_views', with mock.patch(
return_value=[None]): "jenkins_jobs.builder.JenkinsManager.get_views", return_value=[None]
with mock.patch('jenkins_jobs.utils.input', return_value="y"): ):
with mock.patch("jenkins_jobs.utils.input", return_value="y"):
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
@mock.patch('jenkins_jobs.cli.subcommand.update.' @mock.patch("jenkins_jobs.cli.subcommand.update." "JenkinsManager.delete_all_jobs")
'JenkinsManager.delete_all_jobs')
def test_delete_all_abort(self, delete_job_mock): def test_delete_all_abort(self, delete_job_mock):
""" """
Test handling the deletion of a single Jenkins job. Test handling the deletion of a single Jenkins job.
""" """
args = ['--conf', self.default_config_file, 'delete-all'] args = ["--conf", self.default_config_file, "delete-all"]
with mock.patch('jenkins_jobs.utils.input', return_value="n"): with mock.patch("jenkins_jobs.utils.input", return_value="n"):
self.assertRaises(SystemExit, self.assertRaises(SystemExit, self.execute_jenkins_jobs_with_args, args)
self.execute_jenkins_jobs_with_args, args)

View File

@ -21,65 +21,70 @@ from tests.base import mock
from tests.cmd.test_cmd import CmdTestsBase from tests.cmd.test_cmd import CmdTestsBase
@mock.patch('jenkins_jobs.builder.JenkinsManager.get_plugins_info', @mock.patch("jenkins_jobs.builder.JenkinsManager.get_plugins_info", mock.MagicMock)
mock.MagicMock)
class ListFromJenkinsTests(TestWithScenarios, CmdTestsBase): class ListFromJenkinsTests(TestWithScenarios, CmdTestsBase):
scenarios = [ scenarios = [
('single', ("single", dict(jobs=["job1"], globs=[], found=["job1"])),
dict(jobs=['job1'], globs=[], found=['job1'])), ("multiple", dict(jobs=["job1", "job2"], globs=[], found=["job1", "job2"])),
('multiple', (
dict(jobs=['job1', 'job2'], globs=[], found=['job1', 'job2'])), "multiple_with_glob",
('multiple_with_glob', dict(
dict(jobs=['job1', 'job2', 'job3'], globs=["job[1-2]"], jobs=["job1", "job2", "job3"],
found=['job1', 'job2'])), globs=["job[1-2]"],
('multiple_with_multi_glob', found=["job1", "job2"],
dict(jobs=['job1', 'job2', 'job3', 'job4'], ),
globs=["job1", "job[24]"], ),
found=['job1', 'job2', 'job4'])), (
"multiple_with_multi_glob",
dict(
jobs=["job1", "job2", "job3", "job4"],
globs=["job1", "job[24]"],
found=["job1", "job2", "job4"],
),
),
] ]
@mock.patch('jenkins_jobs.builder.JenkinsManager.get_jobs') @mock.patch("jenkins_jobs.builder.JenkinsManager.get_jobs")
def test_list(self, get_jobs_mock): def test_list(self, get_jobs_mock):
def _get_jobs(): def _get_jobs():
return [{'name': name} for name in self.jobs] return [{"name": name} for name in self.jobs]
get_jobs_mock.side_effect = _get_jobs get_jobs_mock.side_effect = _get_jobs
console_out = io.BytesIO() console_out = io.BytesIO()
args = ['--conf', self.default_config_file, 'list'] + self.globs args = ["--conf", self.default_config_file, "list"] + self.globs
with mock.patch('sys.stdout', console_out): with mock.patch("sys.stdout", console_out):
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
self.assertEqual(console_out.getvalue().decode('utf-8').rstrip(), self.assertEqual(
('\n'.join(self.found))) console_out.getvalue().decode("utf-8").rstrip(), ("\n".join(self.found))
)
@mock.patch('jenkins_jobs.builder.JenkinsManager.get_plugins_info', @mock.patch("jenkins_jobs.builder.JenkinsManager.get_plugins_info", mock.MagicMock)
mock.MagicMock)
class ListFromYamlTests(TestWithScenarios, CmdTestsBase): class ListFromYamlTests(TestWithScenarios, CmdTestsBase):
scenarios = [ scenarios = [
('all', ("all", dict(globs=[], found=["bam001", "bar001", "bar002", "baz001"])),
dict(globs=[], found=['bam001', 'bar001', 'bar002', 'baz001'])), (
('some', "some",
dict(globs=["*am*", "*002", "bar001"], dict(
found=['bam001', 'bar001', 'bar002'])), globs=["*am*", "*002", "bar001"], found=["bam001", "bar001", "bar002"]
),
),
] ]
def test_list(self): def test_list(self):
path = os.path.join(self.fixtures_path, 'cmd-002.yaml') path = os.path.join(self.fixtures_path, "cmd-002.yaml")
console_out = io.BytesIO() console_out = io.BytesIO()
with mock.patch('sys.stdout', console_out): with mock.patch("sys.stdout", console_out):
self.execute_jenkins_jobs_with_args( self.execute_jenkins_jobs_with_args(
['--conf', ["--conf", self.default_config_file, "list", "-p", path] + self.globs
self.default_config_file, )
'list',
'-p',
path] + self.globs)
self.assertEqual(console_out.getvalue().decode('utf-8').rstrip(), self.assertEqual(
('\n'.join(self.found))) console_out.getvalue().decode("utf-8").rstrip(), ("\n".join(self.found))
)

View File

@ -35,31 +35,35 @@ from tests.base import mock
from tests.cmd.test_cmd import CmdTestsBase from tests.cmd.test_cmd import CmdTestsBase
@mock.patch('jenkins_jobs.builder.JenkinsManager.get_plugins_info', @mock.patch("jenkins_jobs.builder.JenkinsManager.get_plugins_info", mock.MagicMock)
mock.MagicMock)
class TestTests(CmdTestsBase): class TestTests(CmdTestsBase):
def test_non_existing_job(self): def test_non_existing_job(self):
""" """
Run test mode and pass a non-existing job name Run test mode and pass a non-existing job name
(probably better to fail here) (probably better to fail here)
""" """
args = ['--conf', self.default_config_file, 'test', args = [
os.path.join(self.fixtures_path, "--conf",
'cmd-001.yaml'), self.default_config_file,
'invalid'] "test",
os.path.join(self.fixtures_path, "cmd-001.yaml"),
"invalid",
]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
def test_valid_job(self): def test_valid_job(self):
""" """
Run test mode and pass a valid job name Run test mode and pass a valid job name
""" """
args = ['--conf', self.default_config_file, 'test', args = [
os.path.join(self.fixtures_path, "--conf",
'cmd-001.yaml'), self.default_config_file,
'foo-job'] "test",
os.path.join(self.fixtures_path, "cmd-001.yaml"),
"foo-job",
]
console_out = io.BytesIO() console_out = io.BytesIO()
with mock.patch('sys.stdout', console_out): with mock.patch("sys.stdout", console_out):
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
def test_console_output(self): def test_console_output(self):
@ -68,13 +72,18 @@ class TestTests(CmdTestsBase):
""" """
console_out = io.BytesIO() console_out = io.BytesIO()
with mock.patch('sys.stdout', console_out): with mock.patch("sys.stdout", console_out):
args = ['--conf', self.default_config_file, 'test', args = [
os.path.join(self.fixtures_path, 'cmd-001.yaml')] "--conf",
self.default_config_file,
"test",
os.path.join(self.fixtures_path, "cmd-001.yaml"),
]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
xml_content = io.open(os.path.join(self.fixtures_path, 'cmd-001.xml'), xml_content = io.open(
'r', encoding='utf-8').read() os.path.join(self.fixtures_path, "cmd-001.xml"), "r", encoding="utf-8"
self.assertEqual(console_out.getvalue().decode('utf-8'), xml_content) ).read()
self.assertEqual(console_out.getvalue().decode("utf-8"), xml_content)
def test_output_dir(self): def test_output_dir(self):
""" """
@ -83,11 +92,11 @@ class TestTests(CmdTestsBase):
""" """
tmpdir = tempfile.mkdtemp() tmpdir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, tmpdir) self.addCleanup(shutil.rmtree, tmpdir)
args = ['test', os.path.join(self.fixtures_path, 'cmd-001.yaml'), args = ["test", os.path.join(self.fixtures_path, "cmd-001.yaml"), "-o", tmpdir]
'-o', tmpdir]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
self.expectThat(os.path.join(tmpdir, 'foo-job'), self.expectThat(
testtools.matchers.FileExists()) os.path.join(tmpdir, "foo-job"), testtools.matchers.FileExists()
)
def test_output_dir_config_xml(self): def test_output_dir_config_xml(self):
""" """
@ -96,11 +105,18 @@ class TestTests(CmdTestsBase):
""" """
tmpdir = tempfile.mkdtemp() tmpdir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, tmpdir) self.addCleanup(shutil.rmtree, tmpdir)
args = ['test', os.path.join(self.fixtures_path, 'cmd-001.yaml'), args = [
'-o', tmpdir, '--config-xml'] "test",
os.path.join(self.fixtures_path, "cmd-001.yaml"),
"-o",
tmpdir,
"--config-xml",
]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
self.expectThat(os.path.join(tmpdir, 'foo-job', 'config.xml'), self.expectThat(
testtools.matchers.FileExists()) os.path.join(tmpdir, "foo-job", "config.xml"),
testtools.matchers.FileExists(),
)
def test_stream_input_output_no_encoding_exceed_recursion(self): def test_stream_input_output_no_encoding_exceed_recursion(self):
""" """
@ -109,13 +125,12 @@ class TestTests(CmdTestsBase):
""" """
console_out = io.BytesIO() console_out = io.BytesIO()
input_file = os.path.join(self.fixtures_path, input_file = os.path.join(self.fixtures_path, "large-number-of-jobs-001.yaml")
'large-number-of-jobs-001.yaml') with io.open(input_file, "r") as f:
with io.open(input_file, 'r') as f: with mock.patch("sys.stdout", console_out):
with mock.patch('sys.stdout', console_out):
console_out.encoding = None console_out.encoding = None
with mock.patch('sys.stdin', f): with mock.patch("sys.stdin", f):
args = ['test'] args = ["test"]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
def test_stream_input_output_utf8_encoding(self): def test_stream_input_output_utf8_encoding(self):
@ -125,16 +140,17 @@ class TestTests(CmdTestsBase):
""" """
console_out = io.BytesIO() console_out = io.BytesIO()
input_file = os.path.join(self.fixtures_path, 'cmd-001.yaml') input_file = os.path.join(self.fixtures_path, "cmd-001.yaml")
with io.open(input_file, 'r') as f: with io.open(input_file, "r") as f:
with mock.patch('sys.stdout', console_out): with mock.patch("sys.stdout", console_out):
with mock.patch('sys.stdin', f): with mock.patch("sys.stdin", f):
args = ['--conf', self.default_config_file, 'test'] args = ["--conf", self.default_config_file, "test"]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
xml_content = io.open(os.path.join(self.fixtures_path, 'cmd-001.xml'), xml_content = io.open(
'r', encoding='utf-8').read() os.path.join(self.fixtures_path, "cmd-001.xml"), "r", encoding="utf-8"
value = console_out.getvalue().decode('utf-8') ).read()
value = console_out.getvalue().decode("utf-8")
self.assertEqual(value, xml_content) self.assertEqual(value, xml_content)
def test_stream_input_output_ascii_encoding(self): def test_stream_input_output_ascii_encoding(self):
@ -143,18 +159,19 @@ class TestTests(CmdTestsBase):
ascii encoding with unicode input ascii encoding with unicode input
""" """
console_out = io.BytesIO() console_out = io.BytesIO()
console_out.encoding = 'ascii' console_out.encoding = "ascii"
input_file = os.path.join(self.fixtures_path, 'cmd-001.yaml') input_file = os.path.join(self.fixtures_path, "cmd-001.yaml")
with io.open(input_file, 'r') as f: with io.open(input_file, "r") as f:
with mock.patch('sys.stdout', console_out): with mock.patch("sys.stdout", console_out):
with mock.patch('sys.stdin', f): with mock.patch("sys.stdin", f):
args = ['--conf', self.default_config_file, 'test'] args = ["--conf", self.default_config_file, "test"]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
xml_content = io.open(os.path.join(self.fixtures_path, 'cmd-001.xml'), xml_content = io.open(
'r', encoding='utf-8').read() os.path.join(self.fixtures_path, "cmd-001.xml"), "r", encoding="utf-8"
value = console_out.getvalue().decode('ascii') ).read()
value = console_out.getvalue().decode("ascii")
self.assertEqual(value, xml_content) self.assertEqual(value, xml_content)
def test_stream_output_ascii_encoding_invalid_char(self): def test_stream_output_ascii_encoding_invalid_char(self):
@ -164,65 +181,65 @@ class TestTests(CmdTestsBase):
that cannot be converted. that cannot be converted.
""" """
console_out = io.BytesIO() console_out = io.BytesIO()
console_out.encoding = 'ascii' console_out.encoding = "ascii"
input_file = os.path.join(self.fixtures_path, 'unicode001.yaml') input_file = os.path.join(self.fixtures_path, "unicode001.yaml")
with io.open(input_file, 'r', encoding='utf-8') as f: with io.open(input_file, "r", encoding="utf-8") as f:
with mock.patch('sys.stdout', console_out): with mock.patch("sys.stdout", console_out):
with mock.patch('sys.stdin', f): with mock.patch("sys.stdin", f):
args = ['--conf', self.default_config_file, 'test'] args = ["--conf", self.default_config_file, "test"]
jenkins_jobs = entry.JenkinsJobs(args) jenkins_jobs = entry.JenkinsJobs(args)
e = self.assertRaises(UnicodeError, jenkins_jobs.execute) e = self.assertRaises(UnicodeError, jenkins_jobs.execute)
self.assertIn("'ascii' codec can't encode character", str(e)) self.assertIn("'ascii' codec can't encode character", str(e))
@mock.patch( @mock.patch("jenkins_jobs.cli.subcommand.update.XmlJobGenerator.generateXML")
'jenkins_jobs.cli.subcommand.update.XmlJobGenerator.generateXML') @mock.patch("jenkins_jobs.cli.subcommand.update.ModuleRegistry")
@mock.patch('jenkins_jobs.cli.subcommand.update.ModuleRegistry')
def test_plugins_info_stub_option(self, registry_mock, generateXML_mock): def test_plugins_info_stub_option(self, registry_mock, generateXML_mock):
""" """
Test handling of plugins_info stub option. Test handling of plugins_info stub option.
""" """
plugins_info_stub_yaml_file = os.path.join(self.fixtures_path, plugins_info_stub_yaml_file = os.path.join(
'plugins-info.yaml') self.fixtures_path, "plugins-info.yaml"
args = ['--conf', )
os.path.join(self.fixtures_path, 'cmd-001.conf'), args = [
'test', "--conf",
'-p', os.path.join(self.fixtures_path, "cmd-001.conf"),
plugins_info_stub_yaml_file, "test",
os.path.join(self.fixtures_path, 'cmd-001.yaml')] "-p",
plugins_info_stub_yaml_file,
os.path.join(self.fixtures_path, "cmd-001.yaml"),
]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
with io.open(plugins_info_stub_yaml_file, with io.open(plugins_info_stub_yaml_file, "r", encoding="utf-8") as yaml_file:
'r', encoding='utf-8') as yaml_file:
plugins_info_list = yaml.load(yaml_file) plugins_info_list = yaml.load(yaml_file)
registry_mock.assert_called_with(mock.ANY, registry_mock.assert_called_with(mock.ANY, plugins_info_list)
plugins_info_list)
@mock.patch( @mock.patch("jenkins_jobs.cli.subcommand.update.XmlJobGenerator.generateXML")
'jenkins_jobs.cli.subcommand.update.XmlJobGenerator.generateXML') @mock.patch("jenkins_jobs.cli.subcommand.update.ModuleRegistry")
@mock.patch('jenkins_jobs.cli.subcommand.update.ModuleRegistry') def test_bogus_plugins_info_stub_option(self, registry_mock, generateXML_mock):
def test_bogus_plugins_info_stub_option(self, registry_mock,
generateXML_mock):
""" """
Verify that a JenkinsJobException is raised if the plugins_info stub Verify that a JenkinsJobException is raised if the plugins_info stub
file does not yield a list as its top-level object. file does not yield a list as its top-level object.
""" """
plugins_info_stub_yaml_file = os.path.join(self.fixtures_path, plugins_info_stub_yaml_file = os.path.join(
'bogus-plugins-info.yaml') self.fixtures_path, "bogus-plugins-info.yaml"
args = ['--conf', )
os.path.join(self.fixtures_path, 'cmd-001.conf'), args = [
'test', "--conf",
'-p', os.path.join(self.fixtures_path, "cmd-001.conf"),
plugins_info_stub_yaml_file, "test",
os.path.join(self.fixtures_path, 'cmd-001.yaml')] "-p",
plugins_info_stub_yaml_file,
os.path.join(self.fixtures_path, "cmd-001.yaml"),
]
stderr = StringIO() stderr = StringIO()
with mock.patch('sys.stderr', stderr): with mock.patch("sys.stderr", stderr):
self.assertRaises(SystemExit, entry.JenkinsJobs, args) self.assertRaises(SystemExit, entry.JenkinsJobs, args)
self.assertIn("must contain a Yaml list", self.assertIn("must contain a Yaml list", stderr.getvalue())
stderr.getvalue())
class TestJenkinsGetPluginInfoError(CmdTestsBase): class TestJenkinsGetPluginInfoError(CmdTestsBase):
@ -231,9 +248,8 @@ class TestJenkinsGetPluginInfoError(CmdTestsBase):
jenkins_jobs.builder.JenkinsManager.get_plugins_info jenkins_jobs.builder.JenkinsManager.get_plugins_info
""" """
@mock.patch('jenkins.Jenkins.get_plugins') @mock.patch("jenkins.Jenkins.get_plugins")
def test_console_output_jenkins_connection_failure_warning( def test_console_output_jenkins_connection_failure_warning(self, get_plugins_mock):
self, get_plugins_mock):
""" """
Run test mode and verify that failed Jenkins connection attempt Run test mode and verify that failed Jenkins connection attempt
exception does not bubble out of cmd.main. Ideally, we would also test exception does not bubble out of cmd.main. Ideally, we would also test
@ -242,44 +258,51 @@ class TestJenkinsGetPluginInfoError(CmdTestsBase):
suite. suite.
""" """
get_plugins_mock.side_effect = \ get_plugins_mock.side_effect = jenkins.JenkinsException("Connection refused")
jenkins.JenkinsException("Connection refused") with mock.patch("sys.stdout"):
with mock.patch('sys.stdout'):
try: try:
args = ['--conf', self.default_config_file, 'test', args = [
os.path.join(self.fixtures_path, 'cmd-001.yaml')] "--conf",
self.default_config_file,
"test",
os.path.join(self.fixtures_path, "cmd-001.yaml"),
]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
except jenkins.JenkinsException: except jenkins.JenkinsException:
self.fail("jenkins.JenkinsException propagated to main") self.fail("jenkins.JenkinsException propagated to main")
except Exception: except Exception:
pass # only care about jenkins.JenkinsException for now pass # only care about jenkins.JenkinsException for now
@mock.patch('jenkins.Jenkins.get_plugins') @mock.patch("jenkins.Jenkins.get_plugins")
def test_skip_plugin_retrieval_if_no_config_provided( def test_skip_plugin_retrieval_if_no_config_provided(self, get_plugins_mock):
self, get_plugins_mock):
""" """
Verify that retrieval of information from Jenkins instance about its Verify that retrieval of information from Jenkins instance about its
plugins will be skipped when run if no config file provided. plugins will be skipped when run if no config file provided.
""" """
with mock.patch('sys.stdout', new_callable=io.BytesIO): with mock.patch("sys.stdout", new_callable=io.BytesIO):
args = ['--conf', self.default_config_file, 'test', args = [
os.path.join(self.fixtures_path, 'cmd-001.yaml')] "--conf",
self.default_config_file,
"test",
os.path.join(self.fixtures_path, "cmd-001.yaml"),
]
entry.JenkinsJobs(args) entry.JenkinsJobs(args)
self.assertFalse(get_plugins_mock.called) self.assertFalse(get_plugins_mock.called)
@mock.patch('jenkins.Jenkins.get_plugins_info') @mock.patch("jenkins.Jenkins.get_plugins_info")
def test_skip_plugin_retrieval_if_disabled(self, get_plugins_mock): def test_skip_plugin_retrieval_if_disabled(self, get_plugins_mock):
""" """
Verify that retrieval of information from Jenkins instance about its Verify that retrieval of information from Jenkins instance about its
plugins will be skipped when run if a config file provided and disables plugins will be skipped when run if a config file provided and disables
querying through a config option. querying through a config option.
""" """
with mock.patch('sys.stdout', new_callable=io.BytesIO): with mock.patch("sys.stdout", new_callable=io.BytesIO):
args = ['--conf', args = [
os.path.join(self.fixtures_path, "--conf",
'disable-query-plugins.conf'), os.path.join(self.fixtures_path, "disable-query-plugins.conf"),
'test', "test",
os.path.join(self.fixtures_path, 'cmd-001.yaml')] os.path.join(self.fixtures_path, "cmd-001.yaml"),
]
entry.JenkinsJobs(args) entry.JenkinsJobs(args)
self.assertFalse(get_plugins_mock.called) self.assertFalse(get_plugins_mock.called)
@ -291,8 +314,8 @@ class MatchesDirMissingFilesMismatch(object):
def describe(self): def describe(self):
return "{0} and {1} contain different files".format( return "{0} and {1} contain different files".format(
self.left_directory, self.left_directory, self.right_directory
self.right_directory) )
def get_details(self): def get_details(self):
return {} return {}
@ -308,11 +331,15 @@ class MatchesDirFileContentsMismatch(object):
right_contents = open(self.right_file).readlines() right_contents = open(self.right_file).readlines()
return "{0} is not equal to {1}:\n{2}".format( return "{0} is not equal to {1}:\n{2}".format(
difflib.unified_diff(left_contents, right_contents, difflib.unified_diff(
fromfile=self.left_file, left_contents,
tofile=self.right_file), right_contents,
fromfile=self.left_file,
tofile=self.right_file,
),
self.left_file, self.left_file,
self.right_file) self.right_file,
)
def get_details(self): def get_details(self):
return {} return {}
@ -337,8 +364,7 @@ class MatchesDir(object):
other_files.sort() other_files.sort()
if self.__files != other_files: if self.__files != other_files:
return MatchesDirMissingFilesMismatch(self.__directory, return MatchesDirMissingFilesMismatch(self.__directory, other_directory)
other_directory)
for i, file in enumerate(self.__files): for i, file in enumerate(self.__files):
my_file = os.path.join(self.__directory, file) my_file = os.path.join(self.__directory, file)
@ -349,16 +375,15 @@ class MatchesDir(object):
return None return None
@mock.patch('jenkins_jobs.builder.JenkinsManager.get_plugins_info', @mock.patch("jenkins_jobs.builder.JenkinsManager.get_plugins_info", mock.MagicMock)
mock.MagicMock)
class TestTestsMultiPath(CmdTestsBase): class TestTestsMultiPath(CmdTestsBase):
def setUp(self): def setUp(self):
super(TestTestsMultiPath, self).setUp() super(TestTestsMultiPath, self).setUp()
path_list = [os.path.join(self.fixtures_path, path_list = [
'multi-path/yamldirs/', p) os.path.join(self.fixtures_path, "multi-path/yamldirs/", p)
for p in ['dir1', 'dir2']] for p in ["dir1", "dir2"]
]
self.multipath = os.pathsep.join(path_list) self.multipath = os.pathsep.join(path_list)
self.output_dir = tempfile.mkdtemp() self.output_dir = tempfile.mkdtemp()
@ -374,45 +399,74 @@ class TestTestsMultiPath(CmdTestsBase):
""" """
Run test mode and pass multiple paths. Run test mode and pass multiple paths.
""" """
args = ['--conf', self.default_config_file, 'test', args = [
'-o', self.output_dir, self.multipath] "--conf",
self.default_config_file,
"test",
"-o",
self.output_dir,
self.multipath,
]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
self.check_dirs_match(os.path.join(self.fixtures_path, self.check_dirs_match(
'multi-path/output_simple')) os.path.join(self.fixtures_path, "multi-path/output_simple")
)
def test_recursive_multi_path_command_line(self): def test_recursive_multi_path_command_line(self):
""" """
Run test mode and pass multiple paths with recursive path option. Run test mode and pass multiple paths with recursive path option.
""" """
args = ['--conf', self.default_config_file, 'test', args = [
'-o', self.output_dir, '-r', self.multipath] "--conf",
self.default_config_file,
"test",
"-o",
self.output_dir,
"-r",
self.multipath,
]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
self.check_dirs_match(os.path.join(self.fixtures_path, self.check_dirs_match(
'multi-path/output_recursive')) os.path.join(self.fixtures_path, "multi-path/output_recursive")
)
def test_recursive_multi_path_config_file(self): def test_recursive_multi_path_config_file(self):
# test recursive set in configuration file # test recursive set in configuration file
args = ['--conf', os.path.join(self.fixtures_path, args = [
'multi-path/builder-recursive.ini'), "--conf",
'test', '-o', self.output_dir, self.multipath] 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.execute_jenkins_jobs_with_args(args)
self.check_dirs_match(os.path.join(self.fixtures_path, self.check_dirs_match(
'multi-path/output_recursive')) os.path.join(self.fixtures_path, "multi-path/output_recursive")
)
def test_recursive_multi_path_with_excludes(self): def test_recursive_multi_path_with_excludes(self):
""" """
Run test mode and pass multiple paths with recursive path option. Run test mode and pass multiple paths with recursive path option.
""" """
exclude_path = os.path.join(self.fixtures_path, exclude_path = os.path.join(self.fixtures_path, "multi-path/yamldirs/dir2/dir1")
'multi-path/yamldirs/dir2/dir1') args = [
args = ['--conf', self.default_config_file, 'test', "--conf",
'-x', exclude_path, self.default_config_file,
'-o', self.output_dir, "test",
'-r', self.multipath] "-x",
exclude_path,
"-o",
self.output_dir,
"-r",
self.multipath,
]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
self.check_dirs_match( self.check_dirs_match(
os.path.join(self.fixtures_path, os.path.join(
'multi-path/output_recursive_with_excludes')) self.fixtures_path, "multi-path/output_recursive_with_excludes"
)
)

View File

@ -25,61 +25,60 @@ from tests.base import mock
from tests.cmd.test_cmd import CmdTestsBase from tests.cmd.test_cmd import CmdTestsBase
@mock.patch('jenkins_jobs.builder.JenkinsManager.get_plugins_info', @mock.patch("jenkins_jobs.builder.JenkinsManager.get_plugins_info", mock.MagicMock)
mock.MagicMock)
class UpdateTests(CmdTestsBase): class UpdateTests(CmdTestsBase):
@mock.patch("jenkins_jobs.builder.jenkins.Jenkins.job_exists")
@mock.patch('jenkins_jobs.builder.jenkins.Jenkins.job_exists') @mock.patch("jenkins_jobs.builder.jenkins.Jenkins.get_all_jobs")
@mock.patch('jenkins_jobs.builder.jenkins.Jenkins.get_all_jobs') @mock.patch("jenkins_jobs.builder.jenkins.Jenkins.reconfig_job")
@mock.patch('jenkins_jobs.builder.jenkins.Jenkins.reconfig_job') def test_update_jobs(
def test_update_jobs(self, self, jenkins_reconfig_job, jenkins_get_jobs, jenkins_job_exists
jenkins_reconfig_job, ):
jenkins_get_jobs,
jenkins_job_exists, ):
""" """
Test update_job is called Test update_job is called
""" """
path = os.path.join(self.fixtures_path, 'cmd-002.yaml') path = os.path.join(self.fixtures_path, "cmd-002.yaml")
args = ['--conf', self.default_config_file, 'update', path] args = ["--conf", self.default_config_file, "update", path]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
jenkins_reconfig_job.assert_has_calls( jenkins_reconfig_job.assert_has_calls(
[mock.call(job_name, mock.ANY) [
for job_name in ['bar001', 'bar002', 'baz001', 'bam001']], mock.call(job_name, mock.ANY)
any_order=True for job_name in ["bar001", "bar002", "baz001", "bam001"]
],
any_order=True,
) )
@mock.patch('jenkins_jobs.builder.JenkinsManager.is_job', @mock.patch("jenkins_jobs.builder.JenkinsManager.is_job", return_value=True)
return_value=True) @mock.patch("jenkins_jobs.builder.JenkinsManager.get_jobs")
@mock.patch('jenkins_jobs.builder.JenkinsManager.get_jobs') @mock.patch("jenkins_jobs.builder.JenkinsManager.get_job_md5")
@mock.patch('jenkins_jobs.builder.JenkinsManager.get_job_md5') @mock.patch("jenkins_jobs.builder.JenkinsManager.update_job")
@mock.patch('jenkins_jobs.builder.JenkinsManager.update_job') def test_update_jobs_decode_job_output(
def test_update_jobs_decode_job_output(self, update_job_mock, self, update_job_mock, get_job_md5_mock, get_jobs_mock, is_job_mock
get_job_md5_mock, get_jobs_mock, ):
is_job_mock):
""" """
Test that job xml output has been decoded before attempting to update Test that job xml output has been decoded before attempting to update
""" """
# don't care about the value returned here # don't care about the value returned here
update_job_mock.return_value = ([], 0) update_job_mock.return_value = ([], 0)
path = os.path.join(self.fixtures_path, 'cmd-002.yaml') path = os.path.join(self.fixtures_path, "cmd-002.yaml")
args = ['--conf', self.default_config_file, 'update', path] args = ["--conf", self.default_config_file, "update", path]
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
self.assertTrue(isinstance(update_job_mock.call_args[0][1], self.assertTrue(isinstance(update_job_mock.call_args[0][1], six.text_type))
six.text_type))
@mock.patch('jenkins_jobs.builder.jenkins.Jenkins.job_exists') @mock.patch("jenkins_jobs.builder.jenkins.Jenkins.job_exists")
@mock.patch('jenkins_jobs.builder.jenkins.Jenkins.get_all_jobs') @mock.patch("jenkins_jobs.builder.jenkins.Jenkins.get_all_jobs")
@mock.patch('jenkins_jobs.builder.jenkins.Jenkins.reconfig_job') @mock.patch("jenkins_jobs.builder.jenkins.Jenkins.reconfig_job")
@mock.patch('jenkins_jobs.builder.jenkins.Jenkins.delete_job') @mock.patch("jenkins_jobs.builder.jenkins.Jenkins.delete_job")
def test_update_jobs_and_delete_old(self, def test_update_jobs_and_delete_old(
jenkins_delete_job, self,
jenkins_reconfig_job, jenkins_delete_job,
jenkins_get_all_jobs, jenkins_reconfig_job,
jenkins_job_exists): jenkins_get_all_jobs,
jenkins_job_exists,
):
""" """
Test update behaviour with --delete-old option Test update behaviour with --delete-old option
@ -92,25 +91,26 @@ class UpdateTests(CmdTestsBase):
* mock out a call to jenkins.Jenkins.job_exists() to always return * mock out a call to jenkins.Jenkins.job_exists() to always return
True. True.
""" """
yaml_jobs = ['bar001', 'bar002', 'baz001', 'bam001'] yaml_jobs = ["bar001", "bar002", "baz001", "bam001"]
extra_jobs = ['old_job001', 'old_job002', 'unmanaged'] extra_jobs = ["old_job001", "old_job002", "unmanaged"]
path = os.path.join(self.fixtures_path, 'cmd-002.yaml') path = os.path.join(self.fixtures_path, "cmd-002.yaml")
args = ['--conf', self.default_config_file, 'update', '--delete-old', args = ["--conf", self.default_config_file, "update", "--delete-old", path]
path]
jenkins_get_all_jobs.return_value = [ jenkins_get_all_jobs.return_value = [
{'fullname': name} for name in yaml_jobs + extra_jobs] {"fullname": name} for name in yaml_jobs + extra_jobs
]
with mock.patch('jenkins_jobs.builder.JenkinsManager.is_managed', with mock.patch(
side_effect=(lambda name: name != 'unmanaged')): "jenkins_jobs.builder.JenkinsManager.is_managed",
side_effect=(lambda name: name != "unmanaged"),
):
self.execute_jenkins_jobs_with_args(args) self.execute_jenkins_jobs_with_args(args)
jenkins_reconfig_job.assert_has_calls( jenkins_reconfig_job.assert_has_calls(
[mock.call(job_name, mock.ANY) for job_name in yaml_jobs], [mock.call(job_name, mock.ANY) for job_name in yaml_jobs], any_order=True
any_order=True
) )
calls = [mock.call(name) for name in extra_jobs if name != 'unmanaged'] calls = [mock.call(name) for name in extra_jobs if name != "unmanaged"]
jenkins_delete_job.assert_has_calls(calls) jenkins_delete_job.assert_has_calls(calls)
# to ensure only the calls we expected were made, have to check # to ensure only the calls we expected were made, have to check
# there were no others, as no API call for assert_has_only_calls # there were no others, as no API call for assert_has_only_calls

View File

@ -7,7 +7,7 @@ from tests.base import mock
class CmdTestsBase(base.BaseTestCase): class CmdTestsBase(base.BaseTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
def setUp(self): def setUp(self):
super(CmdTestsBase, self).setUp() super(CmdTestsBase, self).setUp()
@ -17,13 +17,11 @@ class CmdTestsBase(base.BaseTestCase):
# are run in parallel. Stub out the JobCache to ensure that each # are run in parallel. Stub out the JobCache to ensure that each
# test can safely create the cache directory without risk of # test can safely create the cache directory without risk of
# interference. # interference.
cache_patch = mock.patch('jenkins_jobs.builder.JobCache', cache_patch = mock.patch("jenkins_jobs.builder.JobCache", autospec=True)
autospec=True)
self.cache_mock = cache_patch.start() self.cache_mock = cache_patch.start()
self.addCleanup(cache_patch.stop) self.addCleanup(cache_patch.stop)
self.default_config_file = os.path.join(self.fixtures_path, self.default_config_file = os.path.join(self.fixtures_path, "empty_builder.ini")
'empty_builder.ini')
def execute_jenkins_jobs_with_args(self, args): def execute_jenkins_jobs_with_args(self, args):
jenkins_jobs = entry.JenkinsJobs(args) jenkins_jobs = entry.JenkinsJobs(args)
@ -31,10 +29,9 @@ class CmdTestsBase(base.BaseTestCase):
class TestCmd(CmdTestsBase): class TestCmd(CmdTestsBase):
def test_with_empty_args(self): def test_with_empty_args(self):
""" """
User passes no args, should fail with SystemExit User passes no args, should fail with SystemExit
""" """
with mock.patch('sys.stderr'): with mock.patch("sys.stderr"):
self.assertRaises(SystemExit, entry.JenkinsJobs, []) self.assertRaises(SystemExit, entry.JenkinsJobs, [])

View File

@ -9,25 +9,25 @@ from jenkins_jobs.cli import entry
from jenkins_jobs import builder from jenkins_jobs import builder
@mock.patch('jenkins_jobs.builder.JenkinsManager.get_plugins_info', @mock.patch("jenkins_jobs.builder.JenkinsManager.get_plugins_info", mock.MagicMock)
mock.MagicMock)
class TestConfigs(CmdTestsBase): class TestConfigs(CmdTestsBase):
global_conf = '/etc/jenkins_jobs/jenkins_jobs.ini' global_conf = "/etc/jenkins_jobs/jenkins_jobs.ini"
user_conf = os.path.join(os.path.expanduser('~'), '.config', user_conf = os.path.join(
'jenkins_jobs', 'jenkins_jobs.ini') os.path.expanduser("~"), ".config", "jenkins_jobs", "jenkins_jobs.ini"
local_conf = os.path.join(os.path.dirname(__file__), )
'jenkins_jobs.ini') local_conf = os.path.join(os.path.dirname(__file__), "jenkins_jobs.ini")
def test_use_global_config(self): def test_use_global_config(self):
""" """
Verify that JJB uses the global config file by default Verify that JJB uses the global config file by default
""" """
args = ['test', 'foo'] args = ["test", "foo"]
conffp = io.open(self.default_config_file, 'r', encoding='utf-8') conffp = io.open(self.default_config_file, "r", encoding="utf-8")
with patch("os.path.isfile", return_value=True) as m_isfile:
with patch('os.path.isfile', return_value=True) as m_isfile:
def side_effect(path): def side_effect(path):
if path == self.global_conf: if path == self.global_conf:
return True return True
@ -35,36 +35,35 @@ class TestConfigs(CmdTestsBase):
m_isfile.side_effect = side_effect m_isfile.side_effect = side_effect
with patch('io.open', return_value=conffp) as m_open: with patch("io.open", return_value=conffp) as m_open:
entry.JenkinsJobs(args, config_file_required=True) entry.JenkinsJobs(args, config_file_required=True)
m_open.assert_called_with(self.global_conf, 'r', m_open.assert_called_with(self.global_conf, "r", encoding="utf-8")
encoding='utf-8')
def test_use_config_in_user_home(self): def test_use_config_in_user_home(self):
""" """
Verify that JJB uses config file in user home folder Verify that JJB uses config file in user home folder
""" """
args = ['test', 'foo'] 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:
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): def side_effect(path):
if path == self.user_conf: if path == self.user_conf:
return True return True
return False return False
m_isfile.side_effect = side_effect m_isfile.side_effect = side_effect
with patch('io.open', return_value=conffp) as m_open: with patch("io.open", return_value=conffp) as m_open:
entry.JenkinsJobs(args, config_file_required=True) entry.JenkinsJobs(args, config_file_required=True)
m_open.assert_called_with(self.user_conf, 'r', m_open.assert_called_with(self.user_conf, "r", encoding="utf-8")
encoding='utf-8')
def test_non_existing_config_dir(self): def test_non_existing_config_dir(self):
""" """
Run test mode and pass a non-existing configuration directory Run test mode and pass a non-existing configuration directory
""" """
args = ['--conf', self.default_config_file, 'test', 'foo'] args = ["--conf", self.default_config_file, "test", "foo"]
jenkins_jobs = entry.JenkinsJobs(args) jenkins_jobs = entry.JenkinsJobs(args)
self.assertRaises(IOError, jenkins_jobs.execute) self.assertRaises(IOError, jenkins_jobs.execute)
@ -72,8 +71,7 @@ class TestConfigs(CmdTestsBase):
""" """
Run test mode and pass a non-existing configuration file Run test mode and pass a non-existing configuration file
""" """
args = ['--conf', self.default_config_file, 'test', args = ["--conf", self.default_config_file, "test", "non-existing.yaml"]
'non-existing.yaml']
jenkins_jobs = entry.JenkinsJobs(args) jenkins_jobs = entry.JenkinsJobs(args)
self.assertRaises(IOError, jenkins_jobs.execute) self.assertRaises(IOError, jenkins_jobs.execute)
@ -82,37 +80,42 @@ class TestConfigs(CmdTestsBase):
Run test mode and check config settings from conf file retained Run test mode and check config settings from conf file retained
when none of the global CLI options are set. when none of the global CLI options are set.
""" """
config_file = os.path.join(self.fixtures_path, config_file = os.path.join(self.fixtures_path, "settings_from_config.ini")
'settings_from_config.ini') args = ["--conf", config_file, "test", "dummy.yaml"]
args = ['--conf', config_file, 'test', 'dummy.yaml']
jenkins_jobs = entry.JenkinsJobs(args) jenkins_jobs = entry.JenkinsJobs(args)
jjb_config = jenkins_jobs.jjb_config jjb_config = jenkins_jobs.jjb_config
self.assertEqual(jjb_config.jenkins['user'], "jenkins_user") self.assertEqual(jjb_config.jenkins["user"], "jenkins_user")
self.assertEqual(jjb_config.jenkins['password'], "jenkins_password") self.assertEqual(jjb_config.jenkins["password"], "jenkins_password")
self.assertEqual(jjb_config.builder['ignore_cache'], True) self.assertEqual(jjb_config.builder["ignore_cache"], True)
self.assertEqual(jjb_config.builder['flush_cache'], True) self.assertEqual(jjb_config.builder["flush_cache"], True)
self.assertEqual(jjb_config.builder['update'], "all") self.assertEqual(jjb_config.builder["update"], "all")
self.assertEqual( self.assertEqual(jjb_config.yamlparser["allow_empty_variables"], True)
jjb_config.yamlparser['allow_empty_variables'], True)
def test_config_options_overriden_by_cli(self): def test_config_options_overriden_by_cli(self):
""" """
Run test mode and check config settings from conf file retained Run test mode and check config settings from conf file retained
when none of the global CLI options are set. when none of the global CLI options are set.
""" """
args = ['--user', 'myuser', '--password', 'mypassword', args = [
'--ignore-cache', '--flush-cache', '--allow-empty-variables', "--user",
'test', 'dummy.yaml'] "myuser",
"--password",
"mypassword",
"--ignore-cache",
"--flush-cache",
"--allow-empty-variables",
"test",
"dummy.yaml",
]
jenkins_jobs = entry.JenkinsJobs(args) jenkins_jobs = entry.JenkinsJobs(args)
jjb_config = jenkins_jobs.jjb_config jjb_config = jenkins_jobs.jjb_config
self.assertEqual(jjb_config.jenkins['user'], "myuser") self.assertEqual(jjb_config.jenkins["user"], "myuser")
self.assertEqual(jjb_config.jenkins['password'], "mypassword") self.assertEqual(jjb_config.jenkins["password"], "mypassword")
self.assertEqual(jjb_config.builder['ignore_cache'], True) self.assertEqual(jjb_config.builder["ignore_cache"], True)
self.assertEqual(jjb_config.builder['flush_cache'], True) self.assertEqual(jjb_config.builder["flush_cache"], True)
self.assertEqual( self.assertEqual(jjb_config.yamlparser["allow_empty_variables"], True)
jjb_config.yamlparser['allow_empty_variables'], True)
@mock.patch('jenkins_jobs.cli.subcommand.update.JenkinsManager') @mock.patch("jenkins_jobs.cli.subcommand.update.JenkinsManager")
def test_update_timeout_not_set(self, jenkins_mock): def test_update_timeout_not_set(self, jenkins_mock):
"""Check that timeout is left unset """Check that timeout is left unset
@ -120,8 +123,8 @@ class TestConfigs(CmdTestsBase):
provided via the config option. provided via the config option.
""" """
path = os.path.join(self.fixtures_path, 'cmd-002.yaml') path = os.path.join(self.fixtures_path, "cmd-002.yaml")
args = ['--conf', self.default_config_file, 'update', path] args = ["--conf", self.default_config_file, "update", path]
jenkins_mock.return_value.update_jobs.return_value = ([], 0) jenkins_mock.return_value.update_jobs.return_value = ([], 0)
jenkins_mock.return_value.update_views.return_value = ([], 0) jenkins_mock.return_value.update_views.return_value = ([], 0)
@ -131,10 +134,9 @@ class TestConfigs(CmdTestsBase):
# contains the expected timeout value. # contains the expected timeout value.
jjb_config = jenkins_mock.call_args[0][0] jjb_config = jenkins_mock.call_args[0][0]
self.assertEqual(jjb_config.jenkins['timeout'], self.assertEqual(jjb_config.jenkins["timeout"], builder._DEFAULT_TIMEOUT)
builder._DEFAULT_TIMEOUT)
@mock.patch('jenkins_jobs.cli.subcommand.update.JenkinsManager') @mock.patch("jenkins_jobs.cli.subcommand.update.JenkinsManager")
def test_update_timeout_set(self, jenkins_mock): def test_update_timeout_set(self, jenkins_mock):
"""Check that timeout is set correctly """Check that timeout is set correctly
@ -142,10 +144,9 @@ class TestConfigs(CmdTestsBase):
provided via the config option. provided via the config option.
""" """
path = os.path.join(self.fixtures_path, 'cmd-002.yaml') path = os.path.join(self.fixtures_path, "cmd-002.yaml")
config_file = os.path.join(self.fixtures_path, config_file = os.path.join(self.fixtures_path, "non-default-timeout.ini")
'non-default-timeout.ini') args = ["--conf", config_file, "update", path]
args = ['--conf', config_file, 'update', path]
jenkins_mock.return_value.update_jobs.return_value = ([], 0) jenkins_mock.return_value.update_jobs.return_value = ([], 0)
jenkins_mock.return_value.update_views.return_value = ([], 0) jenkins_mock.return_value.update_views.return_value = ([], 0)
@ -155,4 +156,4 @@ class TestConfigs(CmdTestsBase):
# contains the expected timeout value. # contains the expected timeout value.
jjb_config = jenkins_mock.call_args[0][0] jjb_config = jenkins_mock.call_args[0][0]
self.assertEqual(jjb_config.jenkins['timeout'], 0.2) self.assertEqual(jjb_config.jenkins["timeout"], 0.2)

View File

@ -22,6 +22,7 @@ def fake_os_walk(paths):
new_path = "/".join([top, name]) new_path = "/".join([top, name])
for x in os_walk(new_path, topdown): for x in os_walk(new_path, topdown):
yield x yield x
return os_walk return os_walk
@ -29,10 +30,9 @@ def fake_os_walk(paths):
# attempting to create the cache directory multiple times as the tests # attempting to create the cache directory multiple times as the tests
# are run in parallel. Stub out the JobCache to ensure that each # are run in parallel. Stub out the JobCache to ensure that each
# test can safely create the object without effect. # test can safely create the object without effect.
@mock.patch('jenkins_jobs.builder.JobCache', mock.MagicMock) @mock.patch("jenkins_jobs.builder.JobCache", mock.MagicMock)
class CmdRecursePath(testtools.TestCase): class CmdRecursePath(testtools.TestCase):
@mock.patch("jenkins_jobs.utils.os.walk")
@mock.patch('jenkins_jobs.utils.os.walk')
def test_recursive_path_option_exclude_pattern(self, oswalk_mock): def test_recursive_path_option_exclude_pattern(self, oswalk_mock):
""" """
Test paths returned by the recursive processing when using pattern Test paths returned by the recursive processing when using pattern
@ -48,21 +48,21 @@ class CmdRecursePath(testtools.TestCase):
""" """
os_walk_paths = [ os_walk_paths = [
('/jjb_configs', (['dir1', 'dir2', 'dir3', 'test3'], ())), ("/jjb_configs", (["dir1", "dir2", "dir3", "test3"], ())),
('/jjb_configs/dir1', (['test1'], ('file'))), ("/jjb_configs/dir1", (["test1"], ("file"))),
('/jjb_configs/dir2', (['test2'], ())), ("/jjb_configs/dir2", (["test2"], ())),
('/jjb_configs/dir3', (['bar'], ())), ("/jjb_configs/dir3", (["bar"], ())),
('/jjb_configs/dir3/bar', ([], ())), ("/jjb_configs/dir3/bar", ([], ())),
('/jjb_configs/test3/bar', None), ("/jjb_configs/test3/bar", None),
('/jjb_configs/test3/baz', None) ("/jjb_configs/test3/baz", None),
] ]
paths = [k for k, v in os_walk_paths if v is not None] paths = [k for k, v in os_walk_paths if v is not None]
oswalk_mock.side_effect = fake_os_walk(os_walk_paths) oswalk_mock.side_effect = fake_os_walk(os_walk_paths)
self.assertEqual(paths, utils.recurse_path('/jjb_configs', ['test*'])) self.assertEqual(paths, utils.recurse_path("/jjb_configs", ["test*"]))
@mock.patch('jenkins_jobs.utils.os.walk') @mock.patch("jenkins_jobs.utils.os.walk")
def test_recursive_path_option_exclude_absolute(self, oswalk_mock): def test_recursive_path_option_exclude_absolute(self, oswalk_mock):
""" """
Test paths returned by the recursive processing when using absolute Test paths returned by the recursive processing when using absolute
@ -78,25 +78,26 @@ class CmdRecursePath(testtools.TestCase):
""" """
os_walk_paths = [ os_walk_paths = [
('/jjb_configs', (['dir1', 'dir2', 'dir3', 'test3'], ())), ("/jjb_configs", (["dir1", "dir2", "dir3", "test3"], ())),
('/jjb_configs/dir1', None), ("/jjb_configs/dir1", None),
('/jjb_configs/dir2', (['test2'], ())), ("/jjb_configs/dir2", (["test2"], ())),
('/jjb_configs/dir3', (['bar'], ())), ("/jjb_configs/dir3", (["bar"], ())),
('/jjb_configs/test3', (['bar', 'baz'], ())), ("/jjb_configs/test3", (["bar", "baz"], ())),
('/jjb_configs/dir2/test2', ([], ())), ("/jjb_configs/dir2/test2", ([], ())),
('/jjb_configs/dir3/bar', ([], ())), ("/jjb_configs/dir3/bar", ([], ())),
('/jjb_configs/test3/bar', ([], ())), ("/jjb_configs/test3/bar", ([], ())),
('/jjb_configs/test3/baz', ([], ())) ("/jjb_configs/test3/baz", ([], ())),
] ]
paths = [k for k, v in os_walk_paths if v is not None] paths = [k for k, v in os_walk_paths if v is not None]
oswalk_mock.side_effect = fake_os_walk(os_walk_paths) oswalk_mock.side_effect = fake_os_walk(os_walk_paths)
self.assertEqual(paths, utils.recurse_path('/jjb_configs', self.assertEqual(
['/jjb_configs/dir1'])) paths, utils.recurse_path("/jjb_configs", ["/jjb_configs/dir1"])
)
@mock.patch('jenkins_jobs.utils.os.walk') @mock.patch("jenkins_jobs.utils.os.walk")
def test_recursive_path_option_exclude_relative(self, oswalk_mock): def test_recursive_path_option_exclude_relative(self, oswalk_mock):
""" """
Test paths returned by the recursive processing when using relative Test paths returned by the recursive processing when using relative
@ -112,25 +113,27 @@ class CmdRecursePath(testtools.TestCase):
""" """
os_walk_paths = [ os_walk_paths = [
('jjb_configs', (['dir1', 'dir2', 'dir3', 'test3'], ())), ("jjb_configs", (["dir1", "dir2", "dir3", "test3"], ())),
('jjb_configs/dir1', (['test'], ('file'))), ("jjb_configs/dir1", (["test"], ("file"))),
('jjb_configs/dir2', (['test2'], ())), ("jjb_configs/dir2", (["test2"], ())),
('jjb_configs/dir3', (['bar'], ())), ("jjb_configs/dir3", (["bar"], ())),
('jjb_configs/test3', (['bar', 'baz'], ())), ("jjb_configs/test3", (["bar", "baz"], ())),
('jjb_configs/dir1/test', ([], ())), ("jjb_configs/dir1/test", ([], ())),
('jjb_configs/dir2/test2', ([], ())), ("jjb_configs/dir2/test2", ([], ())),
('jjb_configs/dir3/bar', ([], ())), ("jjb_configs/dir3/bar", ([], ())),
('jjb_configs/test3/bar', None), ("jjb_configs/test3/bar", None),
('jjb_configs/test3/baz', ([], ())) ("jjb_configs/test3/baz", ([], ())),
] ]
rel_os_walk_paths = [ rel_os_walk_paths = [
(os.path.abspath( (os.path.abspath(os.path.join(os.path.curdir, k)), v)
os.path.join(os.path.curdir, k)), v) for k, v in os_walk_paths] for k, v in os_walk_paths
]
paths = [k for k, v in rel_os_walk_paths if v is not None] paths = [k for k, v in rel_os_walk_paths if v is not None]
oswalk_mock.side_effect = fake_os_walk(rel_os_walk_paths) oswalk_mock.side_effect = fake_os_walk(rel_os_walk_paths)
self.assertEqual(paths, utils.recurse_path('jjb_configs', self.assertEqual(
['jjb_configs/test3/bar'])) paths, utils.recurse_path("jjb_configs", ["jjb_configs/test3/bar"])
)

View File

@ -23,10 +23,10 @@ from tests.base import mock
class TestCaseModuleDuplicates(base.SingleJobTestCase): class TestCaseModuleDuplicates(base.SingleJobTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
@mock.patch('jenkins_jobs.builder.logger', autospec=True) @mock.patch("jenkins_jobs.builder.logger", autospec=True)
def test_yaml_snippet(self, mock_logger): def test_yaml_snippet(self, mock_logger):
if os.path.basename(self.in_filename).startswith("exception_"): if os.path.basename(self.in_filename).startswith("exception_"):

View File

@ -16,19 +16,19 @@ def dispatch(exc, *args):
def gen_xml(exc, *args): def gen_xml(exc, *args):
data = {'module': 'data'} # noqa data = {"module": "data"} # noqa
raise exc(*args) raise exc(*args)
class TestInvalidAttributeError(base.BaseTestCase): class TestInvalidAttributeError(base.BaseTestCase):
def test_no_valid_values(self): def test_no_valid_values(self):
# When given no valid values, InvalidAttributeError simply displays a # When given no valid values, InvalidAttributeError simply displays a
# message indicating the invalid value, the component type, the # message indicating the invalid value, the component type, the
# component name, and the attribute name. # component name, and the attribute name.
message = "'{0}' is an invalid value for attribute {1}.{2}".format( message = "'{0}' is an invalid value for attribute {1}.{2}".format(
"fnord", "type.name", "fubar") "fnord", "type.name", "fubar"
)
with ExpectedException(errors.InvalidAttributeError, message): with ExpectedException(errors.InvalidAttributeError, message):
dispatch(errors.InvalidAttributeError, "fubar", "fnord") dispatch(errors.InvalidAttributeError, "fubar", "fnord")
@ -37,46 +37,49 @@ class TestInvalidAttributeError(base.BaseTestCase):
# indicating the invalid value, the component type, the component name, # indicating the invalid value, the component type, the component name,
# and the attribute name; additionally, it lists the valid values for # and the attribute name; additionally, it lists the valid values for
# the current component type & name. # the current component type & name.
valid_values = ['herp', 'derp'] valid_values = ["herp", "derp"]
message = "'{0}' is an invalid value for attribute {1}.{2}".format( message = "'{0}' is an invalid value for attribute {1}.{2}".format(
"fnord", "type.name", "fubar") "fnord", "type.name", "fubar"
)
message += "\nValid values include: {0}".format( message += "\nValid values include: {0}".format(
', '.join("'{0}'".format(value) for value in valid_values)) ", ".join("'{0}'".format(value) for value in valid_values)
)
with ExpectedException(errors.InvalidAttributeError, message): with ExpectedException(errors.InvalidAttributeError, message):
dispatch(errors.InvalidAttributeError, "fubar", "fnord", dispatch(errors.InvalidAttributeError, "fubar", "fnord", valid_values)
valid_values)
class TestMissingAttributeError(base.BaseTestCase): class TestMissingAttributeError(base.BaseTestCase):
def test_with_single_missing_attribute(self): def test_with_single_missing_attribute(self):
# When passed a single missing attribute, display a message indicating # When passed a single missing attribute, display a message indicating
# * the missing attribute # * the missing attribute
# * which component type and component name is missing it. # * which component type and component name is missing it.
missing_attribute = 'herp' missing_attribute = "herp"
message = "Missing {0} from an instance of '{1}'".format( message = "Missing {0} from an instance of '{1}'".format(
missing_attribute, 'type.name') missing_attribute, "type.name"
)
with ExpectedException(errors.MissingAttributeError, message): with ExpectedException(errors.MissingAttributeError, message):
dispatch(errors.MissingAttributeError, missing_attribute) dispatch(errors.MissingAttributeError, missing_attribute)
with ExpectedException(errors.MissingAttributeError, with ExpectedException(
message.replace('type.name', 'module')): errors.MissingAttributeError, message.replace("type.name", "module")
):
gen_xml(errors.MissingAttributeError, missing_attribute) gen_xml(errors.MissingAttributeError, missing_attribute)
def test_with_multiple_missing_attributes(self): def test_with_multiple_missing_attributes(self):
# When passed multiple missing attributes, display a message indicating # When passed multiple missing attributes, display a message indicating
# * the missing attributes # * the missing attributes
# * which component type and component name is missing it. # * which component type and component name is missing it.
missing_attribute = ['herp', 'derp'] missing_attribute = ["herp", "derp"]
message = "One of {0} must be present in '{1}'".format( message = "One of {0} must be present in '{1}'".format(
', '.join("'{0}'".format(value) for value in missing_attribute), ", ".join("'{0}'".format(value) for value in missing_attribute), "type.name"
'type.name') )
with ExpectedException(errors.MissingAttributeError, message): with ExpectedException(errors.MissingAttributeError, message):
dispatch(errors.MissingAttributeError, missing_attribute) dispatch(errors.MissingAttributeError, missing_attribute)
with ExpectedException(errors.MissingAttributeError, with ExpectedException(
message.replace('type.name', 'module')): errors.MissingAttributeError, message.replace("type.name", "module")
):
gen_xml(errors.MissingAttributeError, missing_attribute) gen_xml(errors.MissingAttributeError, missing_attribute)

View File

@ -22,6 +22,6 @@ from tests import base
class TestCaseModuleGeneral(base.BaseScenariosTestCase): class TestCaseModuleGeneral(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = general.General klass = general.General

View File

@ -19,6 +19,6 @@ from tests import base
class TestCaseModulePublishers(base.BaseScenariosTestCase): class TestCaseModulePublishers(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = hipchat_notif.HipChat klass = hipchat_notif.HipChat

View File

@ -21,66 +21,65 @@ from tests.base import mock
_plugins_info = {} _plugins_info = {}
_plugins_info['plugin1'] = {'longName': '', _plugins_info["plugin1"] = {"longName": "", "shortName": "", "version": ""}
'shortName': '',
'version': ''}
@mock.patch('jenkins_jobs.builder.JobCache', mock.MagicMock) @mock.patch("jenkins_jobs.builder.JobCache", mock.MagicMock)
class TestCaseTestJenkinsManager(base.BaseTestCase): class TestCaseTestJenkinsManager(base.BaseTestCase):
def setUp(self): def setUp(self):
super(TestCaseTestJenkinsManager, self).setUp() super(TestCaseTestJenkinsManager, self).setUp()
self.jjb_config = JJBConfig() self.jjb_config = JJBConfig()
self.jjb_config.validate() self.jjb_config.validate()
def test_plugins_list(self): def test_plugins_list(self):
self.jjb_config.builder['plugins_info'] = _plugins_info self.jjb_config.builder["plugins_info"] = _plugins_info
self.builder = jenkins_jobs.builder.JenkinsManager(self.jjb_config) self.builder = jenkins_jobs.builder.JenkinsManager(self.jjb_config)
self.assertEqual(self.builder.plugins_list, _plugins_info) self.assertEqual(self.builder.plugins_list, _plugins_info)
@mock.patch.object(jenkins_jobs.builder.jenkins.Jenkins, @mock.patch.object(
'get_plugins', jenkins_jobs.builder.jenkins.Jenkins, "get_plugins", return_value=_plugins_info
return_value=_plugins_info) )
def test_plugins_list_from_jenkins(self, jenkins_mock): def test_plugins_list_from_jenkins(self, jenkins_mock):
# Trigger fetching the plugins from jenkins when accessing the property # Trigger fetching the plugins from jenkins when accessing the property
self.jjb_config.builder['plugins_info'] = {} self.jjb_config.builder["plugins_info"] = {}
self.builder = jenkins_jobs.builder.JenkinsManager(self.jjb_config) self.builder = jenkins_jobs.builder.JenkinsManager(self.jjb_config)
# See https://github.com/formiaczek/multi_key_dict/issues/17 # See https://github.com/formiaczek/multi_key_dict/issues/17
# self.assertEqual(self.builder.plugins_list, k) # self.assertEqual(self.builder.plugins_list, k)
for key_tuple in self.builder.plugins_list.keys(): for key_tuple in self.builder.plugins_list.keys():
for key in key_tuple: for key in key_tuple:
self.assertEqual(self.builder.plugins_list[key], self.assertEqual(self.builder.plugins_list[key], _plugins_info[key])
_plugins_info[key])
def test_delete_managed(self): def test_delete_managed(self):
self.jjb_config.builder['plugins_info'] = {} self.jjb_config.builder["plugins_info"] = {}
self.builder = jenkins_jobs.builder.JenkinsManager(self.jjb_config) self.builder = jenkins_jobs.builder.JenkinsManager(self.jjb_config)
with mock.patch.multiple('jenkins_jobs.builder.JenkinsManager', with mock.patch.multiple(
get_jobs=mock.DEFAULT, "jenkins_jobs.builder.JenkinsManager",
is_job=mock.DEFAULT, get_jobs=mock.DEFAULT,
is_managed=mock.DEFAULT, is_job=mock.DEFAULT,
delete_job=mock.DEFAULT) as patches: is_managed=mock.DEFAULT,
patches['get_jobs'].return_value = [{'fullname': 'job1'}, delete_job=mock.DEFAULT,
{'fullname': 'job2'}] ) as patches:
patches['is_managed'].side_effect = [True, True] patches["get_jobs"].return_value = [
patches['is_job'].side_effect = [True, True] {"fullname": "job1"},
{"fullname": "job2"},
]
patches["is_managed"].side_effect = [True, True]
patches["is_job"].side_effect = [True, True]
self.builder.delete_old_managed() self.builder.delete_old_managed()
self.assertEqual(patches['delete_job'].call_count, 2) self.assertEqual(patches["delete_job"].call_count, 2)
def _get_plugins_info_error_test(self, error_string): def _get_plugins_info_error_test(self, error_string):
builder = jenkins_jobs.builder.JenkinsManager(self.jjb_config) builder = jenkins_jobs.builder.JenkinsManager(self.jjb_config)
exception = jenkins_jobs.builder.jenkins.JenkinsException(error_string) exception = jenkins_jobs.builder.jenkins.JenkinsException(error_string)
with mock.patch.object(builder.jenkins, 'get_plugins', with mock.patch.object(builder.jenkins, "get_plugins", side_effect=exception):
side_effect=exception):
plugins_info = builder.get_plugins_info() plugins_info = builder.get_plugins_info()
self.assertEqual([_plugins_info['plugin1']], plugins_info) self.assertEqual([_plugins_info["plugin1"]], plugins_info)
def test_get_plugins_info_handles_connectionrefused_errors(self): def test_get_plugins_info_handles_connectionrefused_errors(self):
self._get_plugins_info_error_test('Connection refused') self._get_plugins_info_error_test("Connection refused")
def test_get_plugins_info_handles_forbidden_errors(self): def test_get_plugins_info_handles_forbidden_errors(self):
self._get_plugins_info_error_test('Forbidden') self._get_plugins_info_error_test("Forbidden")

View File

@ -21,5 +21,5 @@ from tests import base
class TestCaseModuleJsonParser(base.SingleJobTestCase): class TestCaseModuleJsonParser(base.SingleJobTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path, in_ext='json', out_ext='xml') scenarios = base.get_scenarios(fixtures_path, in_ext="json", out_ext="xml")

View File

@ -34,15 +34,16 @@ class TestCaseLocalYamlInclude(base.JsonTestCase):
Verify application specific tags independently of any changes to Verify application specific tags independently of any changes to
modules XML parsing behaviour modules XML parsing behaviour
""" """
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures')
scenarios = base.get_scenarios(fixtures_path, 'yaml', 'json', fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
filter_func=_exclude_scenarios) scenarios = base.get_scenarios(
fixtures_path, "yaml", "json", filter_func=_exclude_scenarios
)
def test_yaml_snippet(self): def test_yaml_snippet(self):
if os.path.basename(self.in_filename).startswith("exception_"): if os.path.basename(self.in_filename).startswith("exception_"):
with ExpectedException(ComposerError, with ExpectedException(ComposerError, "^found duplicate anchor .*"):
"^found duplicate anchor .*"):
super(TestCaseLocalYamlInclude, self).test_yaml_snippet() super(TestCaseLocalYamlInclude, self).test_yaml_snippet()
else: else:
super(TestCaseLocalYamlInclude, self).test_yaml_snippet() super(TestCaseLocalYamlInclude, self).test_yaml_snippet()
@ -53,13 +54,14 @@ class TestCaseLocalYamlAnchorAlias(base.YamlTestCase):
Verify yaml input is expanded to the expected yaml output when using yaml Verify yaml input is expanded to the expected yaml output when using yaml
anchors and aliases. anchors and aliases.
""" """
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures')
scenarios = base.get_scenarios(fixtures_path, 'iyaml', 'oyaml') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path, "iyaml", "oyaml")
class TestCaseLocalYamlIncludeAnchors(base.BaseTestCase): class TestCaseLocalYamlIncludeAnchors(base.BaseTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
def test_multiple_same_anchor_in_multiple_toplevel_yaml(self): def test_multiple_same_anchor_in_multiple_toplevel_yaml(self):
""" """
@ -70,14 +72,16 @@ class TestCaseLocalYamlIncludeAnchors(base.BaseTestCase):
are treated by the yaml loader as independent. are treated by the yaml loader as independent.
""" """
files = ["custom_same_anchor-001-part1.yaml", files = [
"custom_same_anchor-001-part2.yaml"] "custom_same_anchor-001-part1.yaml",
"custom_same_anchor-001-part2.yaml",
]
jjb_config = JJBConfig() jjb_config = JJBConfig()
jjb_config.jenkins['url'] = 'http://example.com' jjb_config.jenkins["url"] = "http://example.com"
jjb_config.jenkins['user'] = 'jenkins' jjb_config.jenkins["user"] = "jenkins"
jjb_config.jenkins['password'] = 'password' jjb_config.jenkins["password"] = "password"
jjb_config.builder['plugins_info'] = [] jjb_config.builder["plugins_info"] = []
jjb_config.validate() jjb_config.validate()
j = YamlParser(jjb_config) j = YamlParser(jjb_config)
j.load_files([os.path.join(self.fixtures_path, f) for f in files]) j.load_files([os.path.join(self.fixtures_path, f) for f in files])
@ -85,22 +89,20 @@ class TestCaseLocalYamlIncludeAnchors(base.BaseTestCase):
class TestCaseLocalYamlRetainAnchors(base.BaseTestCase): class TestCaseLocalYamlRetainAnchors(base.BaseTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
def test_retain_anchors_default(self): def test_retain_anchors_default(self):
""" """
Verify that anchors are NOT retained across files by default. Verify that anchors are NOT retained across files by default.
""" """
files = ["custom_retain_anchors_include001.yaml", files = ["custom_retain_anchors_include001.yaml", "custom_retain_anchors.yaml"]
"custom_retain_anchors.yaml"]
jjb_config = JJBConfig() jjb_config = JJBConfig()
# use the default value for retain_anchors # use the default value for retain_anchors
jjb_config.validate() jjb_config.validate()
j = YamlParser(jjb_config) j = YamlParser(jjb_config)
with ExpectedException(yaml.composer.ComposerError, with ExpectedException(yaml.composer.ComposerError, "found undefined alias.*"):
"found undefined alias.*"):
j.load_files([os.path.join(self.fixtures_path, f) for f in files]) j.load_files([os.path.join(self.fixtures_path, f) for f in files])
def test_retain_anchors_enabled(self): def test_retain_anchors_enabled(self):
@ -109,11 +111,10 @@ class TestCaseLocalYamlRetainAnchors(base.BaseTestCase):
enabled in the config. enabled in the config.
""" """
files = ["custom_retain_anchors_include001.yaml", files = ["custom_retain_anchors_include001.yaml", "custom_retain_anchors.yaml"]
"custom_retain_anchors.yaml"]
jjb_config = JJBConfig() jjb_config = JJBConfig()
jjb_config.yamlparser['retain_anchors'] = True jjb_config.yamlparser["retain_anchors"] = True
jjb_config.validate() jjb_config.validate()
j = YamlParser(jjb_config) j = YamlParser(jjb_config)
j.load_files([os.path.join(self.fixtures_path, f) for f in files]) j.load_files([os.path.join(self.fixtures_path, f) for f in files])

View File

@ -21,5 +21,5 @@ from tests import base
class TestCaseModuleSCMMacro(base.SingleJobTestCase): class TestCaseModuleSCMMacro(base.SingleJobTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)

View File

@ -9,25 +9,34 @@ from tests import base
class ModuleRegistryPluginInfoTestsWithScenarios( class ModuleRegistryPluginInfoTestsWithScenarios(
testscenarios.TestWithScenarios, base.BaseTestCase): testscenarios.TestWithScenarios, base.BaseTestCase
):
scenarios = [ scenarios = [
('s1', dict(v1='1.0.0', op='__gt__', v2='0.8.0')), ("s1", dict(v1="1.0.0", op="__gt__", v2="0.8.0")),
('s2', dict(v1='1.0.1alpha', op='__gt__', v2='1.0.0')), ("s2", dict(v1="1.0.1alpha", op="__gt__", v2="1.0.0")),
('s3', dict(v1='1.0', op='__eq__', v2='1.0.0')), ("s3", dict(v1="1.0", op="__eq__", v2="1.0.0")),
('s4', dict(v1='1.0', op='__eq__', v2='1.0')), ("s4", dict(v1="1.0", op="__eq__", v2="1.0")),
('s5', dict(v1='1.0', op='__lt__', v2='1.8.0')), ("s5", dict(v1="1.0", op="__lt__", v2="1.8.0")),
('s6', dict(v1='1.0.1alpha', op='__lt__', v2='1.0.1')), ("s6", dict(v1="1.0.1alpha", op="__lt__", v2="1.0.1")),
('s7', dict(v1='1.0alpha', op='__lt__', v2='1.0.0')), ("s7", dict(v1="1.0alpha", op="__lt__", v2="1.0.0")),
('s8', dict(v1='1.0-alpha', op='__lt__', v2='1.0.0')), ("s8", dict(v1="1.0-alpha", op="__lt__", v2="1.0.0")),
('s9', dict(v1='1.1-alpha', op='__gt__', v2='1.0')), ("s9", dict(v1="1.1-alpha", op="__gt__", v2="1.0")),
('s10', dict(v1='1.0-SNAPSHOT', op='__lt__', v2='1.0')), ("s10", dict(v1="1.0-SNAPSHOT", op="__lt__", v2="1.0")),
('s11', dict(v1='1.0.preview', op='__lt__', v2='1.0')), ("s11", dict(v1="1.0.preview", op="__lt__", v2="1.0")),
('s12', dict(v1='1.1-SNAPSHOT', op='__gt__', v2='1.0')), ("s12", dict(v1="1.1-SNAPSHOT", op="__gt__", v2="1.0")),
('s13', dict(v1='1.0a-SNAPSHOT', op='__lt__', v2='1.0a')), ("s13", dict(v1="1.0a-SNAPSHOT", op="__lt__", v2="1.0a")),
('s14', dict(v1='1.4.6-SNAPSHOT (private-0986edd9-example)', (
op='__lt__', v2='1.4.6')), "s14",
('s15', dict(v1='1.4.6-SNAPSHOT (private-0986edd9-example)', dict(
op='__gt__', v2='1.4.5')), v1="1.4.6-SNAPSHOT (private-0986edd9-example)", op="__lt__", v2="1.4.6"
),
),
(
"s15",
dict(
v1="1.4.6-SNAPSHOT (private-0986edd9-example)", op="__gt__", v2="1.4.5"
),
),
] ]
def setUp(self): def setUp(self):
@ -36,13 +45,16 @@ class ModuleRegistryPluginInfoTestsWithScenarios(
jjb_config = JJBConfig() jjb_config = JJBConfig()
jjb_config.validate() jjb_config.validate()
plugin_info = [{'shortName': "HerpDerpPlugin", plugin_info = [
'longName': "Blah Blah Blah Plugin" {"shortName": "HerpDerpPlugin", "longName": "Blah Blah Blah Plugin"}
}] ]
plugin_info.append({'shortName': "JankyPlugin1", plugin_info.append(
'longName': "Not A Real Plugin", {
'version': self.v1 "shortName": "JankyPlugin1",
}) "longName": "Not A Real Plugin",
"version": self.v1,
}
)
self.addDetail("plugin_info", text_content(str(plugin_info))) self.addDetail("plugin_info", text_content(str(plugin_info)))
self.registry = ModuleRegistry(jjb_config, plugin_info) self.registry = ModuleRegistry(jjb_config, plugin_info)
@ -61,7 +73,7 @@ class ModuleRegistryPluginInfoTestsWithScenarios(
plugin_info = self.registry.get_plugin_info(plugin_name) plugin_info = self.registry.get_plugin_info(plugin_name)
self.assertIsInstance(plugin_info, dict) self.assertIsInstance(plugin_info, dict)
self.assertEqual(plugin_info['shortName'], plugin_name) self.assertEqual(plugin_info["shortName"], plugin_name)
def test_get_plugin_info_dict_using_longName(self): def test_get_plugin_info_dict_using_longName(self):
""" """
@ -74,7 +86,7 @@ class ModuleRegistryPluginInfoTestsWithScenarios(
plugin_info = self.registry.get_plugin_info(plugin_name) plugin_info = self.registry.get_plugin_info(plugin_name)
self.assertIsInstance(plugin_info, dict) self.assertIsInstance(plugin_info, dict)
self.assertEqual(plugin_info['longName'], plugin_name) self.assertEqual(plugin_info["longName"], plugin_name)
def test_get_plugin_info_dict_no_plugin(self): def test_get_plugin_info_dict_no_plugin(self):
""" """
@ -101,8 +113,8 @@ class ModuleRegistryPluginInfoTestsWithScenarios(
plugin_info = self.registry.get_plugin_info(plugin_name) plugin_info = self.registry.get_plugin_info(plugin_name)
self.assertIsInstance(plugin_info, dict) self.assertIsInstance(plugin_info, dict)
self.assertEqual(plugin_info['shortName'], plugin_name) self.assertEqual(plugin_info["shortName"], plugin_name)
self.assertEqual(plugin_info['version'], '0') self.assertEqual(plugin_info["version"], "0")
def test_plugin_version_comparison(self): def test_plugin_version_comparison(self):
""" """
@ -117,7 +129,8 @@ class ModuleRegistryPluginInfoTestsWithScenarios(
op = getattr(pkg_resources.parse_version(v1), self.op) op = getattr(pkg_resources.parse_version(v1), self.op)
test = op(pkg_resources.parse_version(self.v2)) test = op(pkg_resources.parse_version(self.v2))
self.assertTrue(test, self.assertTrue(
msg="Unexpectedly found {0} {2} {1} == False " test,
"when comparing versions!" msg="Unexpectedly found {0} {2} {1} == False "
.format(v1, self.v2, self.op)) "when comparing versions!".format(v1, self.v2, self.op),
)

View File

@ -24,86 +24,91 @@ from tests import base
class TestCaseTestHelpers(base.BaseTestCase): class TestCaseTestHelpers(base.BaseTestCase):
def test_convert_mapping_to_xml(self): def test_convert_mapping_to_xml(self):
""" """
Tests the test_convert_mapping_to_xml_fail_required function Tests the test_convert_mapping_to_xml_fail_required function
""" """
# Test default values # Test default values
default_root = XML.Element('testdefault') default_root = XML.Element("testdefault")
default_data = yaml.load("string: hello") default_data = yaml.load("string: hello")
default_mappings = [('default-string', 'defaultString', 'default')] default_mappings = [("default-string", "defaultString", "default")]
convert_mapping_to_xml( convert_mapping_to_xml(
default_root, default_root, default_data, default_mappings, fail_required=True
default_data, )
default_mappings, result = default_root.find("defaultString").text
fail_required=True) self.assertThat(result, Equals("default"))
result = default_root.find('defaultString').text
self.assertThat(result, Equals('default'))
# Test user input # Test user input
user_input_root = XML.Element('testUserInput') user_input_root = XML.Element("testUserInput")
user_input_data = yaml.load("user-input-string: hello") user_input_data = yaml.load("user-input-string: hello")
user_input_mappings = [('user-input-string', 'userInputString', user_input_mappings = [("user-input-string", "userInputString", "user-input")]
'user-input')]
convert_mapping_to_xml( convert_mapping_to_xml(
user_input_root, user_input_data, user_input_mappings, fail_required=True
)
result = user_input_root.find("userInputString").text
self.assertThat(result, Equals("hello"))
# Test missing required input
required_root = XML.Element("testrequired")
required_data = yaml.load("string: hello")
required_mappings = [("required-string", "requiredString", None)]
self.assertRaises(
MissingAttributeError,
convert_mapping_to_xml,
required_root,
required_data,
required_mappings,
fail_required=True,
)
# Test invalid user input for list
user_input_root = XML.Element("testUserInput")
user_input_data = yaml.load("user-input-string: bye")
valid_inputs = ["hello"]
user_input_mappings = [
("user-input-string", "userInputString", "user-input", valid_inputs)
]
self.assertRaises(
InvalidAttributeError,
convert_mapping_to_xml,
user_input_root, user_input_root,
user_input_data, user_input_data,
user_input_mappings, user_input_mappings,
fail_required=True) )
result = user_input_root.find('userInputString').text
self.assertThat(result, Equals('hello'))
# Test missing required input
required_root = XML.Element('testrequired')
required_data = yaml.load("string: hello")
required_mappings = [('required-string', 'requiredString', None)]
self.assertRaises(MissingAttributeError,
convert_mapping_to_xml,
required_root,
required_data,
required_mappings,
fail_required=True)
# Test invalid user input for list
user_input_root = XML.Element('testUserInput')
user_input_data = yaml.load("user-input-string: bye")
valid_inputs = ['hello']
user_input_mappings = [('user-input-string', 'userInputString',
'user-input', valid_inputs)]
self.assertRaises(InvalidAttributeError,
convert_mapping_to_xml,
user_input_root,
user_input_data,
user_input_mappings)
# Test invalid user input for dict # Test invalid user input for dict
user_input_root = XML.Element('testUserInput') user_input_root = XML.Element("testUserInput")
user_input_data = yaml.load("user-input-string: later") user_input_data = yaml.load("user-input-string: later")
valid_inputs = {'hello': 'world'} valid_inputs = {"hello": "world"}
user_input_mappings = [('user-input-string', 'userInputString', user_input_mappings = [
'user-input', valid_inputs)] ("user-input-string", "userInputString", "user-input", valid_inputs)
]
self.assertRaises(InvalidAttributeError, self.assertRaises(
convert_mapping_to_xml, InvalidAttributeError,
user_input_root, convert_mapping_to_xml,
user_input_data, user_input_root,
user_input_mappings) user_input_data,
user_input_mappings,
)
# Test invalid key for dict # Test invalid key for dict
user_input_root = XML.Element('testUserInput') user_input_root = XML.Element("testUserInput")
user_input_data = yaml.load("user-input-string: world") user_input_data = yaml.load("user-input-string: world")
valid_inputs = {'hello': 'world'} valid_inputs = {"hello": "world"}
user_input_mappings = [('user-input-string', 'userInputString', user_input_mappings = [
'user-input', valid_inputs)] ("user-input-string", "userInputString", "user-input", valid_inputs)
]
self.assertRaises(InvalidAttributeError, self.assertRaises(
convert_mapping_to_xml, InvalidAttributeError,
user_input_root, convert_mapping_to_xml,
user_input_data, user_input_root,
user_input_mappings) user_input_data,
user_input_mappings,
)

View File

@ -19,9 +19,9 @@ import os
from jenkins_jobs.modules import project_multibranch from jenkins_jobs.modules import project_multibranch
@mock.patch('uuid.uuid4', mock.Mock(return_value='1-1-1-1-1')) @mock.patch("uuid.uuid4", mock.Mock(return_value="1-1-1-1-1"))
class TestCaseMultibranchPipeline(base.BaseScenariosTestCase): class TestCaseMultibranchPipeline(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
default_config_file = '/dev/null' default_config_file = "/dev/null"
klass = project_multibranch.WorkflowMultiBranch klass = project_multibranch.WorkflowMultiBranch

View File

@ -22,6 +22,6 @@ from tests import base
class TestCaseModuleNotifications(base.BaseScenariosTestCase): class TestCaseModuleNotifications(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = notifications.Notifications klass = notifications.Notifications

View File

@ -30,19 +30,18 @@ class TestCaseParallel(TestCase):
def parallel_test(num_base, num_extra): def parallel_test(num_base, num_extra):
return num_base + num_extra return num_base + num_extra
parallel_args = [{'num_extra': num} for num in range(10)] parallel_args = [{"num_extra": num} for num in range(10)]
result = parallel_test(10, concurrent=parallel_args) result = parallel_test(10, concurrent=parallel_args)
self.assertThat(result, matchers.Equals(expected)) self.assertThat(result, matchers.Equals(expected))
def test_parallel_time_less_than_serial(self): def test_parallel_time_less_than_serial(self):
@concurrent @concurrent
def wait(secs): def wait(secs):
time.sleep(secs) time.sleep(secs)
before = time.time() before = time.time()
# ten threads to make it as fast as possible # ten threads to make it as fast as possible
wait(concurrent=[{'secs': 1} for _ in range(10)], n_workers=10) wait(concurrent=[{"secs": 1} for _ in range(10)], n_workers=10)
after = time.time() after = time.time()
self.assertThat(after - before, matchers.LessThan(5)) self.assertThat(after - before, matchers.LessThan(5))
@ -53,18 +52,16 @@ class TestCaseParallel(TestCase):
def parallel_test(num_base, num_extra): def parallel_test(num_base, num_extra):
return num_base + num_extra return num_base + num_extra
parallel_args = [{'num_extra': num} for num in range(10)] parallel_args = [{"num_extra": num} for num in range(10)]
result = parallel_test(10, concurrent=parallel_args, n_workers=1) result = parallel_test(10, concurrent=parallel_args, n_workers=1)
self.assertThat(result, matchers.Equals(expected)) self.assertThat(result, matchers.Equals(expected))
@mock.patch('jenkins_jobs.parallel.cpu_count', wraps=cpu_count) @mock.patch("jenkins_jobs.parallel.cpu_count", wraps=cpu_count)
def test_use_auto_detect_cores(self, mockCpu_count): def test_use_auto_detect_cores(self, mockCpu_count):
@concurrent @concurrent
def parallel_test(): def parallel_test():
return True return True
result = parallel_test(concurrent=[{} for _ in range(10)], result = parallel_test(concurrent=[{} for _ in range(10)], n_workers=0)
n_workers=0)
self.assertThat(result, matchers.Equals([True for _ in range(10)])) self.assertThat(result, matchers.Equals([True for _ in range(10)]))
mockCpu_count.assert_called_once_with() mockCpu_count.assert_called_once_with()

View File

@ -22,6 +22,6 @@ from tests import base
class TestCaseModuleParameters(base.BaseScenariosTestCase): class TestCaseModuleParameters(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = parameters.Parameters klass = parameters.Parameters

View File

@ -22,6 +22,6 @@ from tests import base
class TestCaseModuleProperties(base.BaseScenariosTestCase): class TestCaseModuleProperties(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = properties.Properties klass = properties.Properties

View File

@ -22,6 +22,6 @@ from tests import base
class TestCaseModulePublishers(base.BaseScenariosTestCase): class TestCaseModulePublishers(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = publishers.Publishers klass = publishers.Publishers

View File

@ -21,6 +21,6 @@ from tests import base
class TestCaseModuleReporters(base.BaseScenariosTestCase): class TestCaseModuleReporters(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = reporters.Reporters klass = reporters.Reporters

View File

@ -22,6 +22,6 @@ from tests import base
class TestCaseModuleSCM(base.BaseScenariosTestCase): class TestCaseModuleSCM(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = scm.SCM klass = scm.SCM

View File

@ -22,6 +22,6 @@ from tests import base
class TestCaseModuleTriggers(base.BaseScenariosTestCase): class TestCaseModuleTriggers(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = triggers.Triggers klass = triggers.Triggers

View File

@ -20,18 +20,18 @@ from tests import base
class TestCaseModuleViewAll(base.BaseScenariosTestCase): class TestCaseModuleViewAll(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = view_all.All klass = view_all.All
class TestCaseModuleViewList(base.BaseScenariosTestCase): class TestCaseModuleViewList(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = view_list.List klass = view_list.List
class TestCaseModuleViewPipeline(base.BaseScenariosTestCase): class TestCaseModuleViewPipeline(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = view_pipeline.Pipeline klass = view_pipeline.Pipeline

View File

@ -22,6 +22,6 @@ from tests import base
class TestCaseModuleWrappers(base.BaseScenariosTestCase): class TestCaseModuleWrappers(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
klass = wrappers.Wrappers klass = wrappers.Wrappers

View File

@ -23,23 +23,23 @@ from tests import base
class TestXmlJobGeneratorExceptions(base.BaseTestCase): class TestXmlJobGeneratorExceptions(base.BaseTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'exceptions') fixtures_path = os.path.join(os.path.dirname(__file__), "exceptions")
def test_invalid_project(self): def test_invalid_project(self):
self.conf_filename = None self.conf_filename = None
config = self._get_config() config = self._get_config()
yp = parser.YamlParser(config) yp = parser.YamlParser(config)
yp.parse(os.path.join(self.fixtures_path, yp.parse(os.path.join(self.fixtures_path, "invalid_project.yaml"))
"invalid_project.yaml"))
reg = registry.ModuleRegistry(config) reg = registry.ModuleRegistry(config)
job_data, _ = yp.expandYaml(reg) job_data, _ = yp.expandYaml(reg)
# Generate the XML tree # Generate the XML tree
xml_generator = xml_config.XmlJobGenerator(reg) xml_generator = xml_config.XmlJobGenerator(reg)
e = self.assertRaises(errors.JenkinsJobsException, e = self.assertRaises(
xml_generator.generateXML, job_data) errors.JenkinsJobsException, xml_generator.generateXML, job_data
)
self.assertIn("Unrecognized project-type:", str(e)) self.assertIn("Unrecognized project-type:", str(e))
def test_invalid_view(self): def test_invalid_view(self):
@ -54,8 +54,9 @@ class TestXmlJobGeneratorExceptions(base.BaseTestCase):
# Generate the XML tree # Generate the XML tree
xml_generator = xml_config.XmlViewGenerator(reg) xml_generator = xml_config.XmlViewGenerator(reg)
e = self.assertRaises(errors.JenkinsJobsException, e = self.assertRaises(
xml_generator.generateXML, view_data) errors.JenkinsJobsException, xml_generator.generateXML, view_data
)
self.assertIn("Unrecognized view-type:", str(e)) self.assertIn("Unrecognized view-type:", str(e))
def test_incorrect_template_params(self): def test_incorrect_template_params(self):
@ -63,8 +64,7 @@ class TestXmlJobGeneratorExceptions(base.BaseTestCase):
config = self._get_config() config = self._get_config()
yp = parser.YamlParser(config) yp = parser.YamlParser(config)
yp.parse(os.path.join(self.fixtures_path, yp.parse(os.path.join(self.fixtures_path, "failure_formatting_component.yaml"))
"failure_formatting_component.yaml"))
reg = registry.ModuleRegistry(config) reg = registry.ModuleRegistry(config)
reg.set_parser_data(yp.data) reg.set_parser_data(yp.data)

View File

@ -24,20 +24,19 @@ from tests import base
class TestCaseModuleYamlInclude(base.SingleJobTestCase): class TestCaseModuleYamlInclude(base.SingleJobTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') fixtures_path = os.path.join(os.path.dirname(__file__), "fixtures")
scenarios = base.get_scenarios(fixtures_path) scenarios = base.get_scenarios(fixtures_path)
class TestYamlParserExceptions(base.BaseTestCase): class TestYamlParserExceptions(base.BaseTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'exceptions') fixtures_path = os.path.join(os.path.dirname(__file__), "exceptions")
def test_incorrect_template_dimensions(self): def test_incorrect_template_dimensions(self):
self.conf_filename = None self.conf_filename = None
config = self._get_config() config = self._get_config()
yp = parser.YamlParser(config) yp = parser.YamlParser(config)
yp.parse(os.path.join(self.fixtures_path, yp.parse(os.path.join(self.fixtures_path, "incorrect_template_dimensions.yaml"))
"incorrect_template_dimensions.yaml"))
reg = registry.ModuleRegistry(config) reg = registry.ModuleRegistry(config)
@ -47,23 +46,22 @@ class TestYamlParserExceptions(base.BaseTestCase):
class TestYamlParserFailureFormattingExceptions(base.BaseScenariosTestCase): class TestYamlParserFailureFormattingExceptions(base.BaseScenariosTestCase):
fixtures_path = os.path.join(os.path.dirname(__file__), 'exceptions') fixtures_path = os.path.join(os.path.dirname(__file__), "exceptions")
scenarios = [ scenarios = [("s1", {"name": "template"}), ("s2", {"name": "params"})]
('s1', {'name': 'template'}),
('s2', {'name': 'params'})
]
def test_yaml_snippet(self): def test_yaml_snippet(self):
self.conf_filename = None self.conf_filename = None
config = self._get_config() config = self._get_config()
yp = parser.YamlParser(config) yp = parser.YamlParser(config)
yp.parse(os.path.join(self.fixtures_path, yp.parse(
"failure_formatting_{}.yaml".format(self.name))) os.path.join(
self.fixtures_path, "failure_formatting_{}.yaml".format(self.name)
)
)
reg = registry.ModuleRegistry(config) reg = registry.ModuleRegistry(config)
self.assertRaises(Exception, yp.expandYaml, reg) self.assertRaises(Exception, yp.expandYaml, reg)
self.assertIn("Failure formatting {}".format(self.name), self.assertIn("Failure formatting {}".format(self.name), self.logger.output)
self.logger.output)
self.assertIn("Problem formatting with args", self.logger.output) self.assertIn("Problem formatting with args", self.logger.output)