scms: MultiSCM is generated for scms hidden in macro

This patch fixes problem with generating MultiSCM in case that scms are
encapsulated in macro.
This patch includes simple tests to cover if-branches for this change.

Example:

- scm:
  name: xxx
  scm:
    - git: ...

- scm:
  name: yyy
  scm:
    - git: ...

- scm:
  name: xxx-and-yyy
  scm:
    - xxx
    - yyy

- job:
  name: my-job
  scm:
    - xxx-and-yyy

Change-Id: If3e7b3d19e51545a5623ee060fb7673a7a989233
Signed-off-by: Lukas Bednar <lbednar@redhat.com>
Closes-bug: #1285515
This commit is contained in:
Lukas Bednar 2014-01-29 10:44:40 +01:00
parent 564953609c
commit 34ea32ba80
13 changed files with 374 additions and 56 deletions

View File

@ -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.
<https://wiki.jenkins-ci.org/display/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.
<https://wiki.jenkins-ci.org/display/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)

View File

@ -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)
)

0
tests/macros/__init__.py Normal file
View File

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<actions/>
<description>&lt;!-- Managed by Jenkins Job Builder --&gt;</description>
<keepDependencies>false</keepDependencies>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<concurrentBuild>false</concurrentBuild>
<canRoam>true</canRoam>
<properties/>
<scm class="org.jenkinsci.plugins.multiplescms.MultiSCM">
<scms>
<scm class="hudson.plugins.git.GitSCM">
<configVersion>2</configVersion>
<userRemoteConfigs>
<hudson.plugins.git.UserRemoteConfig>
<name>origin</name>
<refspec>+refs/heads/*:refs/remotes/origin/*</refspec>
<url>ssh://jenkins@review.openstack.org:29418/first.git</url>
</hudson.plugins.git.UserRemoteConfig>
</userRemoteConfigs>
<branches>
<hudson.plugins.git.BranchSpec>
<name>origin/master</name>
</hudson.plugins.git.BranchSpec>
</branches>
<excludedUsers/>
<buildChooser class="hudson.plugins.git.util.DefaultBuildChooser"/>
<disableSubmodules>false</disableSubmodules>
<recursiveSubmodules>false</recursiveSubmodules>
<doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
<authorOrCommitter>false</authorOrCommitter>
<clean>false</clean>
<wipeOutWorkspace>true</wipeOutWorkspace>
<pruneBranches>false</pruneBranches>
<remotePoll>false</remotePoll>
<gitTool>Default</gitTool>
<submoduleCfg class="list"/>
<relativeTargetDir/>
<reference/>
<gitConfigName/>
<gitConfigEmail/>
<skipTag>false</skipTag>
<scmName/>
<useShallowClone>false</useShallowClone>
</scm>
<scm class="hudson.plugins.git.GitSCM">
<configVersion>2</configVersion>
<userRemoteConfigs>
<hudson.plugins.git.UserRemoteConfig>
<name>origin</name>
<refspec>+refs/heads/*:refs/remotes/origin/*</refspec>
<url>ssh://jenkins@review.openstack.org:29418/second.git</url>
</hudson.plugins.git.UserRemoteConfig>
</userRemoteConfigs>
<branches>
<hudson.plugins.git.BranchSpec>
<name>origin/master</name>
</hudson.plugins.git.BranchSpec>
</branches>
<excludedUsers/>
<buildChooser class="hudson.plugins.git.util.DefaultBuildChooser"/>
<disableSubmodules>false</disableSubmodules>
<recursiveSubmodules>false</recursiveSubmodules>
<doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
<authorOrCommitter>false</authorOrCommitter>
<clean>false</clean>
<wipeOutWorkspace>true</wipeOutWorkspace>
<pruneBranches>false</pruneBranches>
<remotePoll>false</remotePoll>
<gitTool>Default</gitTool>
<submoduleCfg class="list"/>
<relativeTargetDir/>
<reference/>
<gitConfigName/>
<gitConfigEmail/>
<skipTag>false</skipTag>
<scmName/>
<useShallowClone>false</useShallowClone>
</scm>
</scms>
</scm>
<builders/>
<publishers/>
<buildWrappers/>
</project>

View File

@ -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

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<actions/>
<description>&lt;!-- Managed by Jenkins Job Builder --&gt;</description>
<keepDependencies>false</keepDependencies>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<concurrentBuild>false</concurrentBuild>
<canRoam>true</canRoam>
<properties/>
<scm class="org.jenkinsci.plugins.multiplescms.MultiSCM">
<scms>
<scm class="hudson.plugins.repo.RepoScm">
<manifestRepositoryUrl>https://example.com/project/</manifestRepositoryUrl>
<manifestBranch>stable</manifestBranch>
<manifestFile>repo.xml</manifestFile>
<manifestGroup>drivers</manifestGroup>
<destinationDir>build</destinationDir>
<repoUrl>https://internal.net/projects/repo</repoUrl>
<mirrorDir>~/git/project/</mirrorDir>
<jobs>3</jobs>
<currentBranch>false</currentBranch>
<quiet>false</quiet>
</scm>
<scm class="hudson.plugins.git.GitSCM">
<configVersion>2</configVersion>
<userRemoteConfigs>
<hudson.plugins.git.UserRemoteConfig>
<name>origin</name>
<refspec>+refs/heads/*:refs/remotes/origin/*</refspec>
<url>ssh://jenkins@review.openstack.org:29418/second.git</url>
</hudson.plugins.git.UserRemoteConfig>
</userRemoteConfigs>
<branches>
<hudson.plugins.git.BranchSpec>
<name>origin/master</name>
</hudson.plugins.git.BranchSpec>
</branches>
<excludedUsers/>
<buildChooser class="hudson.plugins.git.util.DefaultBuildChooser"/>
<disableSubmodules>false</disableSubmodules>
<recursiveSubmodules>false</recursiveSubmodules>
<doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
<authorOrCommitter>false</authorOrCommitter>
<clean>false</clean>
<wipeOutWorkspace>true</wipeOutWorkspace>
<pruneBranches>false</pruneBranches>
<remotePoll>false</remotePoll>
<gitTool>Default</gitTool>
<submoduleCfg class="list"/>
<relativeTargetDir/>
<reference/>
<gitConfigName/>
<gitConfigEmail/>
<skipTag>false</skipTag>
<scmName/>
<useShallowClone>false</useShallowClone>
</scm>
</scms>
</scm>
<builders/>
<publishers/>
<buildWrappers/>
</project>

View File

@ -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

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<actions/>
<description>&lt;!-- Managed by Jenkins Job Builder --&gt;</description>
<keepDependencies>false</keepDependencies>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<concurrentBuild>false</concurrentBuild>
<canRoam>true</canRoam>
<properties/>
<scm class="hudson.scm.NullSCM"/>
<builders/>
<publishers/>
<buildWrappers/>
</project>

View File

@ -0,0 +1,3 @@
- job:
name: my-job
scm: !!seq []

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<actions/>
<description>&lt;!-- Managed by Jenkins Job Builder --&gt;</description>
<keepDependencies>false</keepDependencies>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<concurrentBuild>false</concurrentBuild>
<canRoam>true</canRoam>
<properties/>
<scm class="hudson.plugins.git.GitSCM">
<configVersion>2</configVersion>
<userRemoteConfigs>
<hudson.plugins.git.UserRemoteConfig>
<name>origin</name>
<refspec>+refs/heads/*:refs/remotes/origin/*</refspec>
<url>ssh://jenkins@review.openstack.org:29418/repository.git</url>
</hudson.plugins.git.UserRemoteConfig>
</userRemoteConfigs>
<branches>
<hudson.plugins.git.BranchSpec>
<name>origin/master</name>
</hudson.plugins.git.BranchSpec>
</branches>
<excludedUsers/>
<buildChooser class="hudson.plugins.git.util.DefaultBuildChooser"/>
<disableSubmodules>false</disableSubmodules>
<recursiveSubmodules>false</recursiveSubmodules>
<doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
<authorOrCommitter>false</authorOrCommitter>
<clean>false</clean>
<wipeOutWorkspace>true</wipeOutWorkspace>
<pruneBranches>false</pruneBranches>
<remotePoll>false</remotePoll>
<gitTool>Default</gitTool>
<submoduleCfg class="list"/>
<relativeTargetDir/>
<reference/>
<gitConfigName/>
<gitConfigEmail/>
<skipTag>false</skipTag>
<scmName/>
<useShallowClone>false</useShallowClone>
</scm>
<builders/>
<publishers/>
<buildWrappers/>
</project>

View File

@ -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

View File

@ -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)

View File

@ -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)
)