diff --git a/jenkins_jobs/modules/scm.py b/jenkins_jobs/modules/scm.py index 20531cfd4..97a07b308 100644 --- a/jenkins_jobs/modules/scm.py +++ b/jenkins_jobs/modules/scm.py @@ -18,14 +18,17 @@ The SCM module allows you to specify the source code location for the project. It adds the ``scm`` attribute to the :ref:`Job` definition, which accepts any number of scm definitions. -Note: Adding more than one scm definition requires the Jenkins `Multiple -SCMs plugin. -`_ - **Component**: scm :Macro: scm :Entry Point: jenkins_jobs.scm +The scm module allows referencing multiple repositories in a Jenkins job. +Note: Adding more than one scm definition requires the Jenkins `Multiple +SCMs plugin. +`_ + +Example of multiple repositories in a single job: + .. literalinclude:: /../../tests/macros/scm/multi-scms001.yaml """ @@ -537,14 +540,16 @@ class SCM(jenkins_jobs.modules.base.Base): component_list_type = 'scm' def gen_xml(self, parser, xml_parent, data): - scms = data.get('scm', []) - if scms: - if len(scms) > 1: - class_name = 'org.jenkinsci.plugins.multiplescms.MultiSCM' - xml_attribs = {'class': class_name} - xml_parent = XML.SubElement(xml_parent, 'scm', xml_attribs) - xml_parent = XML.SubElement(xml_parent, 'scms') - for scm in data.get('scm', []): - self.registry.dispatch('scm', parser, xml_parent, scm) - else: + scms_parent = XML.Element('scms') + for scm in data.get('scm', []): + self.registry.dispatch('scm', parser, scms_parent, scm) + scms_count = len(scms_parent) + if scms_count == 0: XML.SubElement(xml_parent, 'scm', {'class': 'hudson.scm.NullSCM'}) + elif scms_count == 1: + xml_parent.append(scms_parent[0]) + else: + class_name = 'org.jenkinsci.plugins.multiplescms.MultiSCM' + xml_attribs = {'class': class_name} + xml_parent = XML.SubElement(xml_parent, 'scm', xml_attribs) + xml_parent.append(scms_parent) diff --git a/tests/base.py b/tests/base.py index 2c72fe9fb..2b73e2f28 100644 --- a/tests/base.py +++ b/tests/base.py @@ -22,8 +22,10 @@ import logging import os import re import doctest +import operator import testtools import xml.etree.ElementTree as XML +from ConfigParser import ConfigParser import yaml from jenkins_jobs.builder import XmlJob, YamlParser, ModuleRegistry from jenkins_jobs.modules import (project_flow, @@ -120,3 +122,40 @@ class BaseTestCase(object): doctest.NORMALIZE_WHITESPACE | doctest.REPORT_NDIFF) ) + + +class SingleJobTestCase(BaseTestCase): + def test_yaml_snippet(self): + if not self.xml_filename or not self.yaml_filename: + return + + xml_filepath = os.path.join(self.fixtures_path, self.xml_filename) + expected_xml = u"%s" % open(xml_filepath, 'r').read() + + yaml_filepath = os.path.join(self.fixtures_path, self.yaml_filename) + + if self.conf_filename: + config = ConfigParser() + conf_filepath = os.path.join(self.fixtures_path, + self.conf_filename) + config.readfp(open(conf_filepath)) + else: + config = None + parser = YamlParser(config) + parser.parse(yaml_filepath) + + # Generate the XML tree + parser.generateXML() + + parser.jobs.sort(key=operator.attrgetter('name')) + + # Prettify generated XML + pretty_xml = "\n".join(job.output() for job in parser.jobs) + + self.assertThat( + pretty_xml, + testtools.matchers.DocTestMatches(expected_xml, + doctest.ELLIPSIS | + doctest.NORMALIZE_WHITESPACE | + doctest.REPORT_NDIFF) + ) diff --git a/tests/macros/__init__.py b/tests/macros/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/macros/scm/multi-scms001.xml b/tests/macros/scm/multi-scms001.xml new file mode 100644 index 000000000..aa2f3f504 --- /dev/null +++ b/tests/macros/scm/multi-scms001.xml @@ -0,0 +1,86 @@ + + + + <!-- Managed by Jenkins Job Builder --> + false + false + false + false + true + + + + + 2 + + + origin + +refs/heads/*:refs/remotes/origin/* + ssh://jenkins@review.openstack.org:29418/first.git + + + + + origin/master + + + + + false + false + false + false + false + true + false + false + Default + + + + + + false + + false + + + 2 + + + origin + +refs/heads/*:refs/remotes/origin/* + ssh://jenkins@review.openstack.org:29418/second.git + + + + + origin/master + + + + + false + false + false + false + false + true + false + false + Default + + + + + + false + + false + + + + + + + diff --git a/tests/macros/scm/multi-scms001.yaml b/tests/macros/scm/multi-scms001.yaml new file mode 100644 index 000000000..c35f42b73 --- /dev/null +++ b/tests/macros/scm/multi-scms001.yaml @@ -0,0 +1,26 @@ +- scm: + name: first-scm + scm: + - git: + url: ssh://jenkins@review.openstack.org:29418/first.git + branches: + - origin/master + +- scm: + name: second-scm + scm: + - git: + url: ssh://jenkins@review.openstack.org:29418/second.git + branches: + - origin/master + +- scm: + name: first-and-second + scm: + - first-scm + - second-scm + +- job: + name: my-job + scm: + - first-and-second diff --git a/tests/macros/scm/multi-scms002.xml b/tests/macros/scm/multi-scms002.xml new file mode 100644 index 000000000..7f3e46089 --- /dev/null +++ b/tests/macros/scm/multi-scms002.xml @@ -0,0 +1,64 @@ + + + + <!-- Managed by Jenkins Job Builder --> + false + false + false + false + true + + + + + https://example.com/project/ + stable + repo.xml + drivers + build + https://internal.net/projects/repo + ~/git/project/ + 3 + false + false + + + 2 + + + origin + +refs/heads/*:refs/remotes/origin/* + ssh://jenkins@review.openstack.org:29418/second.git + + + + + origin/master + + + + + false + false + false + false + false + true + false + false + Default + + + + + + false + + false + + + + + + + diff --git a/tests/macros/scm/multi-scms002.yaml b/tests/macros/scm/multi-scms002.yaml new file mode 100644 index 000000000..559fc5426 --- /dev/null +++ b/tests/macros/scm/multi-scms002.yaml @@ -0,0 +1,33 @@ +- scm: + name: first-scm + scm: + - repo: + manifest-url: https://example.com/project/ + manifest-branch: stable + manifest-file: repo.xml + manifest-group: drivers + destination-dir: build + repo-url: https://internal.net/projects/repo + mirror-dir: ~/git/project/ + jobs: 3 + current-branch: false + quiet: false + +- scm: + name: second-scm + scm: + - git: + url: ssh://jenkins@review.openstack.org:29418/second.git + branches: + - origin/master + +- scm: + name: first-and-second + scm: + - first-scm + - second-scm + +- job: + name: my-job + scm: + - first-and-second diff --git a/tests/macros/scm/scm001.xml b/tests/macros/scm/scm001.xml new file mode 100644 index 000000000..0570e10a5 --- /dev/null +++ b/tests/macros/scm/scm001.xml @@ -0,0 +1,15 @@ + + + + <!-- Managed by Jenkins Job Builder --> + false + false + false + false + true + + + + + + diff --git a/tests/macros/scm/scm001.yaml b/tests/macros/scm/scm001.yaml new file mode 100644 index 000000000..672a865e0 --- /dev/null +++ b/tests/macros/scm/scm001.yaml @@ -0,0 +1,3 @@ +- job: + name: my-job + scm: !!seq [] diff --git a/tests/macros/scm/scm002.xml b/tests/macros/scm/scm002.xml new file mode 100644 index 000000000..96758eb12 --- /dev/null +++ b/tests/macros/scm/scm002.xml @@ -0,0 +1,48 @@ + + + + <!-- Managed by Jenkins Job Builder --> + false + false + false + false + true + + + 2 + + + origin + +refs/heads/*:refs/remotes/origin/* + ssh://jenkins@review.openstack.org:29418/repository.git + + + + + origin/master + + + + + false + false + false + false + false + true + false + false + Default + + + + + + false + + false + + + + + diff --git a/tests/macros/scm/scm002.yaml b/tests/macros/scm/scm002.yaml new file mode 100644 index 000000000..223dcc3bf --- /dev/null +++ b/tests/macros/scm/scm002.yaml @@ -0,0 +1,12 @@ +- scm: + name: repository + scm: + - git: + url: ssh://jenkins@review.openstack.org:29418/repository.git + branches: + - origin/master + +- job: + name: my-job + scm: + - repository diff --git a/tests/macros/test_macros.py b/tests/macros/test_macros.py new file mode 100644 index 000000000..b486dfa90 --- /dev/null +++ b/tests/macros/test_macros.py @@ -0,0 +1,26 @@ +# Joint copyright: +# - Copyright 2012,2013 Wikimedia Foundation +# - Copyright 2012,2013 Antoine "hashar" Musso +# - Copyright 2013 Arnaud Fabre +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +from testtools import TestCase +from testscenarios.testcase import TestWithScenarios +from tests.base import get_scenarios, SingleJobTestCase + + +class TestCaseModuleSCMMacro(TestWithScenarios, TestCase, SingleJobTestCase): + fixtures_path = os.path.join(os.path.dirname(__file__), 'scm') + scenarios = get_scenarios(fixtures_path) diff --git a/tests/yamlparser/test_yamlparser.py b/tests/yamlparser/test_yamlparser.py index 395d45152..99a345b59 100644 --- a/tests/yamlparser/test_yamlparser.py +++ b/tests/yamlparser/test_yamlparser.py @@ -15,52 +15,13 @@ # License for the specific language governing permissions and limitations # under the License. -import operator import os -from ConfigParser import ConfigParser from testtools import TestCase from testscenarios.testcase import TestWithScenarios -from tests.base import get_scenarios, BaseTestCase -import doctest -import testtools -from jenkins_jobs.builder import YamlParser +from tests.base import get_scenarios, SingleJobTestCase -class TestCaseModuleYamlInclude(TestWithScenarios, TestCase, BaseTestCase): +class TestCaseModuleYamlInclude(TestWithScenarios, TestCase, + SingleJobTestCase): fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures') scenarios = get_scenarios(fixtures_path) - - def test_yaml_snippet(self): - if not self.xml_filename or not self.yaml_filename: - return - - xml_filepath = os.path.join(self.fixtures_path, self.xml_filename) - expected_xml = u"%s" % open(xml_filepath, 'r').read() - - yaml_filepath = os.path.join(self.fixtures_path, self.yaml_filename) - - if self.conf_filename: - config = ConfigParser() - conf_filepath = os.path.join(self.fixtures_path, - self.conf_filename) - config.readfp(open(conf_filepath)) - else: - config = None - parser = YamlParser(config) - parser.parse(yaml_filepath) - - # Generate the XML tree - parser.generateXML() - - parser.jobs.sort(key=operator.attrgetter('name')) - - # Prettify generated XML - pretty_xml = "\n".join(job.output() for job in parser.jobs) - - self.assertThat( - pretty_xml, - testtools.matchers.DocTestMatches(expected_xml, - doctest.ELLIPSIS | - doctest.NORMALIZE_WHITESPACE | - doctest.REPORT_NDIFF) - )