badab02647
In one of the recent change to Git plugin behavior[1], tags are
also search as part of branches.
This cause projects that build on all branches to also build on all
tags.
This change allowing for tags to be ignored.
Default this option is false and missing from XML output.
[1] https://github.com/jenkinsci/git-plugin/pull/340
[2] bfeda3e661
Change-Id: I2ed2290f9ef8ecd0d9e96aad1d7cbce2964bf2da
1434 lines
63 KiB
Python
1434 lines
63 KiB
Python
# Copyright 2012 Hewlett-Packard Development Company, L.P.
|
|
#
|
|
# 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.
|
|
|
|
|
|
"""
|
|
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. It is also possible to pass
|
|
``[]`` to the ``scm`` attribute. This is useful when a set of configs has a
|
|
global default ``scm`` and you want to a particular job to override that
|
|
default with no SCM.
|
|
|
|
**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
|
|
:jenkins-wiki:`Multiple SCMs plugin <Multiple+SCMs+Plugin>`.
|
|
|
|
Example of multiple repositories in a single job:
|
|
.. literalinclude:: /../../tests/macros/fixtures/scm/multi-scms001.yaml
|
|
|
|
Example of an empty ``scm``:
|
|
.. literalinclude:: /../../tests/scm/fixtures/empty.yaml
|
|
"""
|
|
|
|
import logging
|
|
import xml.etree.ElementTree as XML
|
|
|
|
from jenkins_jobs.errors import InvalidAttributeError
|
|
from jenkins_jobs.errors import JenkinsJobsException
|
|
from jenkins_jobs.errors import MissingAttributeError
|
|
import jenkins_jobs.modules.base
|
|
from jenkins_jobs.modules.helpers import convert_mapping_to_xml
|
|
|
|
|
|
def git(registry, xml_parent, data):
|
|
"""yaml: git
|
|
Specifies the git SCM repository for this job.
|
|
Requires the Jenkins :jenkins-wiki:`Git Plugin <Git+Plugin>`.
|
|
|
|
:arg str url: URL of the git repository
|
|
:arg str credentials-id: ID of credential to use to connect, which is the
|
|
last field (a 32-digit hexadecimal code) of the path of URL visible
|
|
after you clicked the credential under Jenkins Global credentials.
|
|
(optional)
|
|
:arg str refspec: refspec to fetch (default
|
|
'+refs/heads/\*:refs/remotes/remoteName/\*')
|
|
:arg str name: name to fetch (default 'origin')
|
|
:arg list(str) remotes: list of remotes to set up (optional, only needed if
|
|
multiple remotes need to be set up)
|
|
|
|
:Remote:
|
|
* **url** (`string`) - url of remote repo
|
|
* **refspec** (`string`) - refspec to fetch (optional)
|
|
* **credentials-id** - ID of credential to use to connect, which
|
|
is the last field of the path of URL (a 32-digit hexadecimal
|
|
code) visible after you clicked credential under Jenkins Global
|
|
credentials. (optional)
|
|
:arg list(str) branches: list of branch specifiers to build (default '**')
|
|
:arg bool skip-tag: Skip tagging (default true)
|
|
|
|
.. deprecated:: 1.6.0. Please use per-build-tag extension, which has
|
|
the inverse meaning.
|
|
|
|
:arg bool clean: Clean after checkout (default false)
|
|
|
|
.. deprecated:: 1.1.1. Please use clean extension format.
|
|
|
|
:arg bool fastpoll: Use fast remote polling (default false)
|
|
:arg bool disable-submodules: Disable submodules (default false)
|
|
|
|
.. deprecated:: 1.1.1. Please use submodule extension.
|
|
|
|
:arg bool recursive-submodules: Recursively update submodules (default
|
|
false)
|
|
|
|
.. deprecated:: 1.1.1. Please use submodule extension.
|
|
|
|
:arg str git-tool: The name of the Git installation to use (default
|
|
'Default')
|
|
:arg str reference-repo: Path of the reference repo to use during clone
|
|
(optional)
|
|
:arg str browser: what repository browser to use.
|
|
|
|
:browsers supported:
|
|
* **auto** - (default)
|
|
* **assemblaweb** - https://www.assembla.com/home
|
|
* **bitbucketweb** - https://bitbucket.org/
|
|
* **cgit** - https://git.zx2c4.com/cgit/about/
|
|
* **fisheye** - https://www.atlassian.com/software/fisheye
|
|
* **gitblit** - http://gitblit.com/
|
|
* **githubweb** - https://github.com/
|
|
* **gitiles** - https://code.google.com/p/gitiles/
|
|
* **gitlab** - https://about.gitlab.com/
|
|
* **gitlist** - http://gitlist.org/
|
|
* **gitoriousweb** - https://gitorious.org/
|
|
* **gitweb** - https://git-scm.com/docs/gitweb
|
|
* **kiln** - https://www.fogcreek.com/kiln/
|
|
* **microsoft\-tfs\-2013** - |tfs_2013|
|
|
* **phabricator** - http://phabricator.org/
|
|
* **redmineweb** - http://www.redmine.org/
|
|
* **rhodecode** - https://rhodecode.com/
|
|
* **stash** - https://www.atlassian.com/software/bitbucket/server
|
|
* **viewgit** - http://viewgit.fealdia.org/
|
|
:arg str browser-url: url for the repository browser (required if browser
|
|
is not 'auto', no default)
|
|
:arg str browser-version: version of the repository browser (GitLab only,
|
|
default '0.0')
|
|
:arg str project-name: project name in Gitblit and ViewGit repobrowser
|
|
(optional)
|
|
:arg str repo-name: repository name in phabricator repobrowser (optional)
|
|
:arg str git-config-name: Configure name for Git clone (optional)
|
|
:arg str git-config-email: Configure email for Git clone (optional)
|
|
|
|
:extensions:
|
|
|
|
* **basedir** (`string`) - Location relative to the workspace root to
|
|
clone to (default workspace)
|
|
* **changelog-against** (`dict`)
|
|
* **remote** (`string`) - name of repo that contains branch to
|
|
create changelog against (default 'origin')
|
|
* **branch** (`string`) - name of the branch to create changelog
|
|
against (default 'master')
|
|
* **choosing-strategy**: (`string`) - Jenkins class for selecting what
|
|
to build. Can be one of `default`,`inverse`, or `gerrit`
|
|
(default 'default')
|
|
* **clean** (`dict`)
|
|
* **after** (`bool`) - Clean the workspace after checkout
|
|
* **before** (`bool`) - Clean the workspace before checkout
|
|
* **excluded-users**: (`list(string)`) - list of users to ignore
|
|
revisions from when polling for changes.
|
|
(if polling is enabled, optional)
|
|
* **included-regions**: (`list(string)`) - list of file/folders to
|
|
include (optional)
|
|
* **excluded-regions**: (`list(string)`) - list of file/folders to
|
|
exclude (optional)
|
|
* **ignore-commits-with-messages** (`list(str)`) - Revisions committed
|
|
with messages matching these patterns will be ignored. (optional)
|
|
* **ignore-notify**: (`bool`) - Ignore notifyCommit URL accesses
|
|
(default false)
|
|
* **force-polling-using-workspace** (`bool`) - Force polling using
|
|
workspace (default false)
|
|
* **local-branch** (`string`) - Checkout/merge to local branch
|
|
(optional)
|
|
* **merge** (`dict`)
|
|
* **remote** (`string`) - name of repo that contains branch to
|
|
merge to (default 'origin')
|
|
* **branch** (`string`) - name of the branch to merge to
|
|
* **strategy** (`string`) - merge strategy. Can be one of
|
|
'default', 'resolve', 'recursive', 'octopus', 'ours',
|
|
'subtree'. (default 'default')
|
|
* **fast-forward-mode** (`string`) - merge fast-forward mode.
|
|
Can be one of 'FF', 'FF_ONLY' or 'NO_FF'. (default 'FF')
|
|
* **per-build-tag** (`bool`) - Create a tag in the workspace for every
|
|
build. (default is inverse of skip-tag if set, otherwise false)
|
|
* **prune** (`bool`) - Prune remote branches (default false)
|
|
* **scm-name** (`string`) - The unique scm name for this Git SCM
|
|
(optional)
|
|
* **shallow-clone** (`bool`) - Perform shallow clone (default false)
|
|
* **do-not-fetch-tags** (`bool`) - Perform a clone without tags
|
|
(default false)
|
|
* **sparse-checkout** (`dict`)
|
|
* **paths** (`list`) - List of paths to sparse checkout. (optional)
|
|
* **submodule** (`dict`)
|
|
* **disable** (`bool`) - By disabling support for submodules you
|
|
can still keep using basic git plugin functionality and just have
|
|
Jenkins to ignore submodules completely as if they didn't exist.
|
|
* **recursive** (`bool`) - Retrieve all submodules recursively
|
|
(uses '--recursive' option which requires git>=1.6.5)
|
|
* **tracking** (`bool`) - Retrieve the tip of the configured
|
|
branch in .gitmodules (Uses '\-\-remote' option which requires
|
|
git>=1.8.2)
|
|
* **parent-credentials** (`bool`) - Use credentials from default
|
|
remote of parent repository (default false).
|
|
* **reference-repo** (`str`) - Path of the reference repo to use
|
|
during clone (optional)
|
|
* **timeout** (`int`) - Specify a timeout (in minutes) for
|
|
submodules operations (default 10).
|
|
* **timeout** (`str`) - Timeout for git commands in minutes (optional)
|
|
* **use-author** (`bool`): Use author rather than committer in Jenkin's
|
|
build changeset (default false)
|
|
* **wipe-workspace** (`bool`) - Wipe out workspace before build
|
|
(default true)
|
|
|
|
|
|
Example:
|
|
|
|
.. literalinclude:: /../../tests/scm/fixtures/git001.yaml
|
|
|
|
.. |tfs_2013| replace::
|
|
https://www.visualstudio.com/en-us/products/tfs-overview-vs.aspx
|
|
|
|
"""
|
|
logger = logging.getLogger("%s:git" % __name__)
|
|
|
|
# XXX somebody should write the docs for those with option name =
|
|
# None so we have a sensible name/key for it.
|
|
mapping = [
|
|
# option, xml name, default value (text), attributes (hard coded)
|
|
("disable-submodules", 'disableSubmodules', False),
|
|
("recursive-submodules", 'recursiveSubmodules', False),
|
|
(None, 'doGenerateSubmoduleConfigurations', False),
|
|
# XXX is this the same as force-polling-using-workspace?
|
|
("fastpoll", 'remotePoll', False),
|
|
# XXX does this option still exist?
|
|
("git-tool", 'gitTool', "Default"),
|
|
(None, 'submoduleCfg', '', {'class': 'list'}),
|
|
('reference-repo', 'reference', ''),
|
|
("git-config-name", 'gitConfigName', ''),
|
|
("git-config-email", 'gitConfigEmail', ''),
|
|
]
|
|
|
|
choosing_strategies = {
|
|
'default': 'hudson.plugins.git.util.DefaultBuildChooser',
|
|
'gerrit': ('com.sonyericsson.hudson.plugins.'
|
|
'gerrit.trigger.hudsontrigger.GerritTriggerBuildChooser'),
|
|
'inverse': 'hudson.plugins.git.util.InverseBuildChooser',
|
|
}
|
|
|
|
scm = XML.SubElement(xml_parent,
|
|
'scm', {'class': 'hudson.plugins.git.GitSCM'})
|
|
XML.SubElement(scm, 'configVersion').text = '2'
|
|
user = XML.SubElement(scm, 'userRemoteConfigs')
|
|
if 'remotes' not in data:
|
|
data['remotes'] = [{data.get('name', 'origin'): data.copy()}]
|
|
for remoteData in data['remotes']:
|
|
huser = XML.SubElement(user, 'hudson.plugins.git.UserRemoteConfig')
|
|
remoteName = next(iter(remoteData.keys()))
|
|
XML.SubElement(huser, 'name').text = remoteName
|
|
remoteParams = next(iter(remoteData.values()))
|
|
if 'refspec' in remoteParams:
|
|
refspec = remoteParams['refspec']
|
|
else:
|
|
refspec = '+refs/heads/*:refs/remotes/' + remoteName + '/*'
|
|
XML.SubElement(huser, 'refspec').text = refspec
|
|
if 'url' in remoteParams:
|
|
remoteURL = remoteParams['url']
|
|
else:
|
|
raise JenkinsJobsException('Must specify a url for git remote \"' +
|
|
remoteName + '"')
|
|
XML.SubElement(huser, 'url').text = remoteURL
|
|
if 'credentials-id' in remoteParams:
|
|
credentialsId = remoteParams['credentials-id']
|
|
XML.SubElement(huser, 'credentialsId').text = credentialsId
|
|
xml_branches = XML.SubElement(scm, 'branches')
|
|
branches = data.get('branches', ['**'])
|
|
for branch in branches:
|
|
bspec = XML.SubElement(xml_branches, 'hudson.plugins.git.BranchSpec')
|
|
XML.SubElement(bspec, 'name').text = branch
|
|
for elem in mapping:
|
|
(optname, xmlname, val) = elem[:3]
|
|
|
|
# Throw warning for deprecated settings and skip if the 'submodule' key
|
|
# is available.
|
|
submodule_cfgs = ['disable-submodules', 'recursive-submodules']
|
|
if optname in submodule_cfgs:
|
|
if optname in data:
|
|
logger.warning(
|
|
"'{0}' is deprecated, please convert to use the "
|
|
"'submodule' section instead as support for this "
|
|
"top level option will be removed in a future "
|
|
"release.".format(optname))
|
|
if 'submodule' in data:
|
|
continue
|
|
|
|
attrs = {}
|
|
if len(elem) >= 4:
|
|
attrs = elem[3]
|
|
xe = XML.SubElement(scm, xmlname, attrs)
|
|
if optname and optname in data:
|
|
val = data[optname]
|
|
if type(val) == bool:
|
|
xe.text = str(val).lower()
|
|
else:
|
|
xe.text = val
|
|
|
|
exts_node = XML.SubElement(scm, 'extensions')
|
|
impl_prefix = 'hudson.plugins.git.extensions.impl.'
|
|
|
|
if 'basedir' in data:
|
|
ext = XML.SubElement(exts_node,
|
|
impl_prefix + 'RelativeTargetDirectory')
|
|
XML.SubElement(ext, 'relativeTargetDir').text = data['basedir']
|
|
if 'changelog-against' in data:
|
|
ext_name = impl_prefix + 'ChangelogToBranch'
|
|
ext = XML.SubElement(exts_node, ext_name)
|
|
opts = XML.SubElement(ext, 'options')
|
|
change_remote = data['changelog-against'].get('remote', 'origin')
|
|
change_branch = data['changelog-against'].get('branch', 'master')
|
|
XML.SubElement(opts, 'compareRemote').text = change_remote
|
|
XML.SubElement(opts, 'compareTarget').text = change_branch
|
|
if 'choosing-strategy' in data:
|
|
try:
|
|
choosing_strategy = choosing_strategies[
|
|
data.get('choosing-strategy')]
|
|
except KeyError:
|
|
raise ValueError('Invalid choosing-strategy %r' %
|
|
data.get('choosing-strategy'))
|
|
ext = XML.SubElement(exts_node, impl_prefix + 'BuildChooserSetting')
|
|
XML.SubElement(ext, 'buildChooser', {'class': choosing_strategy})
|
|
if 'clean' in data:
|
|
# Keep support for old format 'clean' configuration by checking
|
|
# if 'clean' is boolean. Else we're using the new extensions style.
|
|
if isinstance(data['clean'], bool):
|
|
clean_after = data['clean']
|
|
clean_before = False
|
|
logger.warning(
|
|
"'clean: bool' configuration format is deprecated, "
|
|
"please use the extension style format to configure "
|
|
"this option.")
|
|
else:
|
|
clean_after = data['clean'].get('after', False)
|
|
clean_before = data['clean'].get('before', False)
|
|
if clean_after:
|
|
ext_name = impl_prefix + 'CleanCheckout'
|
|
ext = XML.SubElement(exts_node, ext_name)
|
|
if clean_before:
|
|
ext_name = impl_prefix + 'CleanBeforeCheckout'
|
|
ext = XML.SubElement(exts_node, ext_name)
|
|
if 'excluded-users' in data:
|
|
excluded_users = '\n'.join(data['excluded-users'])
|
|
ext = XML.SubElement(exts_node, impl_prefix + 'UserExclusion')
|
|
XML.SubElement(ext, 'excludedUsers').text = excluded_users
|
|
if 'included-regions' in data or 'excluded-regions' in data:
|
|
ext = XML.SubElement(exts_node,
|
|
'hudson.plugins.git.extensions.impl.'
|
|
'PathRestriction')
|
|
if 'included-regions' in data:
|
|
include_string = '\n'.join(data['included-regions'])
|
|
XML.SubElement(ext, 'includedRegions').text = include_string
|
|
if 'excluded-regions' in data:
|
|
exclude_string = '\n'.join(data['excluded-regions'])
|
|
XML.SubElement(ext, 'excludedRegions').text = exclude_string
|
|
if 'ignore-commits-with-messages' in data:
|
|
for msg in data['ignore-commits-with-messages']:
|
|
ext_name = impl_prefix + 'MessageExclusion'
|
|
ext = XML.SubElement(exts_node, ext_name)
|
|
XML.SubElement(ext, 'excludedMessage').text = msg
|
|
if 'local-branch' in data:
|
|
ext = XML.SubElement(exts_node, impl_prefix + 'LocalBranch')
|
|
XML.SubElement(ext, 'localBranch').text = str(data['local-branch'])
|
|
if 'merge' in data:
|
|
merge = data['merge']
|
|
merge_strategies = ['default', 'resolve', 'recursive', 'octopus',
|
|
'ours', 'subtree']
|
|
fast_forward_modes = ['FF', 'FF_ONLY', 'NO_FF']
|
|
name = merge.get('remote', 'origin')
|
|
branch = merge['branch']
|
|
ext = XML.SubElement(exts_node, impl_prefix + 'PreBuildMerge')
|
|
merge_opts = XML.SubElement(ext, 'options')
|
|
XML.SubElement(merge_opts, 'mergeRemote').text = name
|
|
XML.SubElement(merge_opts, 'mergeTarget').text = branch
|
|
strategy = merge.get('strategy', 'default')
|
|
if strategy not in merge_strategies:
|
|
raise InvalidAttributeError('strategy', strategy, merge_strategies)
|
|
XML.SubElement(merge_opts, 'mergeStrategy').text = strategy
|
|
fast_forward_mode = merge.get('fast-forward-mode', 'FF')
|
|
if fast_forward_mode not in fast_forward_modes:
|
|
raise InvalidAttributeError('fast-forward-mode', fast_forward_mode,
|
|
fast_forward_modes)
|
|
XML.SubElement(merge_opts, 'fastForwardMode').text = fast_forward_mode
|
|
if 'scm-name' in data:
|
|
ext = XML.SubElement(exts_node, impl_prefix + 'ScmName')
|
|
XML.SubElement(ext, 'name').text = str(data['scm-name'])
|
|
clone_options = (
|
|
"shallow-clone",
|
|
"timeout",
|
|
"do-not-fetch-tags"
|
|
)
|
|
if any(key in data for key in clone_options):
|
|
clo = XML.SubElement(exts_node, impl_prefix + 'CloneOption')
|
|
XML.SubElement(clo, 'shallow').text = str(
|
|
data.get('shallow-clone', False)).lower()
|
|
if 'do-not-fetch-tags' in data:
|
|
XML.SubElement(clo, 'noTags').text = str(
|
|
data.get('do-not-fetch-tags', False)).lower()
|
|
if 'timeout' in data:
|
|
XML.SubElement(clo, 'timeout').text = str(data['timeout'])
|
|
if 'sparse-checkout' in data:
|
|
ext_name = impl_prefix + 'SparseCheckoutPaths'
|
|
ext = XML.SubElement(exts_node, ext_name)
|
|
sparse_co = XML.SubElement(ext, 'sparseCheckoutPaths')
|
|
sparse_paths = data['sparse-checkout'].get('paths')
|
|
if sparse_paths is not None:
|
|
path_tagname = impl_prefix + 'SparseCheckoutPath'
|
|
for path in sparse_paths:
|
|
path_tag = XML.SubElement(sparse_co, path_tagname)
|
|
XML.SubElement(path_tag, 'path').text = path
|
|
if 'submodule' in data:
|
|
ext_name = impl_prefix + 'SubmoduleOption'
|
|
ext = XML.SubElement(exts_node, ext_name)
|
|
XML.SubElement(ext, 'disableSubmodules').text = str(
|
|
data['submodule'].get('disable', False)).lower()
|
|
XML.SubElement(ext, 'recursiveSubmodules').text = str(
|
|
data['submodule'].get('recursive', False)).lower()
|
|
XML.SubElement(ext, 'trackingSubmodules').text = str(
|
|
data['submodule'].get('tracking', False)).lower()
|
|
XML.SubElement(ext, 'parentCredentials').text = str(
|
|
data['submodule'].get('parent-credentials', False)).lower()
|
|
XML.SubElement(ext, 'reference').text = str(
|
|
data['submodule'].get('reference-repo', ''))
|
|
XML.SubElement(ext, 'timeout').text = str(
|
|
data['submodule'].get('timeout', 10))
|
|
if 'timeout' in data:
|
|
co = XML.SubElement(exts_node, impl_prefix + 'CheckoutOption')
|
|
XML.SubElement(co, 'timeout').text = str(data['timeout'])
|
|
|
|
polling_using_workspace = str(data.get('force-polling-using-workspace',
|
|
False)).lower()
|
|
if polling_using_workspace == 'true':
|
|
ext_name = impl_prefix + 'DisableRemotePoll'
|
|
ext = XML.SubElement(exts_node, ext_name)
|
|
if 'per-build-tag' in data or 'skip-tag' in data:
|
|
# We want to support both skip-tag (the old option) and per-build-tag
|
|
# (the new option), with the new one overriding the old one.
|
|
# Unfortunately they have inverse meanings, so we have to be careful.
|
|
# The default value of per-build-tag is False if skip-tag is not set,
|
|
# so we set the default value of skip-tag to True.
|
|
per_build_tag_default = False
|
|
if str(data.get('skip-tag', True)).lower == 'false':
|
|
per_build_tag_default = True
|
|
if str(data.get('per-build-tag',
|
|
per_build_tag_default)).lower() == 'true':
|
|
XML.SubElement(exts_node, impl_prefix + 'PerBuildTag')
|
|
prune = str(data.get('prune', False)).lower()
|
|
if prune == 'true':
|
|
XML.SubElement(exts_node, impl_prefix + 'PruneStaleBranch')
|
|
ignore_notify_commits = str(data.get('ignore-notify', False)).lower()
|
|
if ignore_notify_commits == 'true':
|
|
XML.SubElement(exts_node, impl_prefix + 'IgnoreNotifyCommit')
|
|
# By default we wipe the workspace
|
|
wipe_workspace = str(data.get('wipe-workspace', True)).lower()
|
|
if wipe_workspace == 'true':
|
|
ext_name = impl_prefix + 'WipeWorkspace'
|
|
ext = XML.SubElement(exts_node, ext_name)
|
|
|
|
use_author = str(data.get('use-author', False)).lower()
|
|
if use_author == 'true':
|
|
XML.SubElement(exts_node, impl_prefix + 'AuthorInChangelog')
|
|
|
|
browser = data.get('browser', 'auto')
|
|
browserdict = {'auto': 'auto',
|
|
'assemblaweb': 'AssemblaWeb',
|
|
'bitbucketweb': 'BitbucketWeb',
|
|
'cgit': 'CGit',
|
|
'fisheye': 'FisheyeGitRepositoryBrowser',
|
|
'gitblit': 'GitBlitRepositoryBrowser',
|
|
'githubweb': 'GithubWeb',
|
|
'gitiles': 'Gitiles',
|
|
'gitlab': 'GitLab',
|
|
'gitlist': 'GitList',
|
|
'gitoriousweb': 'GitoriousWeb',
|
|
'gitweb': 'GitWeb',
|
|
'kiln': 'KilnGit',
|
|
'microsoft-tfs-2013': 'TFS2013GitRepositoryBrowser',
|
|
'phabricator': 'Phabricator',
|
|
'redmineweb': 'RedmineWeb',
|
|
'rhodecode': 'RhodeCode',
|
|
'stash': 'Stash',
|
|
'viewgit': 'ViewGitWeb'}
|
|
if browser not in browserdict:
|
|
valid = sorted(browserdict.keys())
|
|
raise JenkinsJobsException("Browser entered is not valid must be one "
|
|
"of: %s or %s." % (", ".join(valid[:-1]),
|
|
valid[-1]))
|
|
if browser != 'auto':
|
|
bc = XML.SubElement(scm, 'browser', {'class':
|
|
'hudson.plugins.git.browser.' +
|
|
browserdict[browser]})
|
|
XML.SubElement(bc, 'url').text = data['browser-url']
|
|
if browser in ['gitblit', 'viewgit']:
|
|
XML.SubElement(bc, 'projectName').text = str(
|
|
data.get('project-name', ''))
|
|
if browser == 'gitlab':
|
|
XML.SubElement(bc, 'version').text = str(
|
|
data.get('browser-version', '0.0'))
|
|
if browser == 'phabricator':
|
|
XML.SubElement(bc, 'repo').text = str(
|
|
data.get('repo-name', ''))
|
|
|
|
|
|
def cvs(registry, xml_parent, data):
|
|
"""yaml: cvs
|
|
Specifies the CVS SCM repository for this job.
|
|
Requires the Jenkins :jenkins-wiki:`CVS Plugin <CVS+Plugin>`.
|
|
|
|
:arg list repos: List of CVS repositories. (required)
|
|
|
|
:Repos:
|
|
* **root** (`str`) -- The CVS connection string Jenkins uses to
|
|
connect to the server. The format is :protocol:user@host:path
|
|
(required)
|
|
* **locations** (`list`) -- List of locations. (required)
|
|
|
|
:Locations:
|
|
* **type** (`str`) -- Type of location.
|
|
|
|
:supported values:
|
|
* **HEAD** - (default)
|
|
* **BRANCH**
|
|
* **TAG**
|
|
* **name** (`str`) -- Name of location. Only valid in case
|
|
of 'BRANCH' or 'TAG' location type. (default '')
|
|
* **use-head** (`bool`) -- Use Head if not found. Only
|
|
valid in case of 'BRANCH' or 'TAG' location type.
|
|
(default false)
|
|
* **modules** (`list`) -- List of modules. (required)
|
|
|
|
:Modules:
|
|
* **remote** -- The name of the module in the
|
|
repository at CVSROOT. (required)
|
|
* **local-name** -- The name to be applied to
|
|
this module in the local workspace. If blank,
|
|
the remote module name will be used.
|
|
(default '')
|
|
* **excluded-regions** (`list str`) -- Patterns for excluding
|
|
regions. (optional)
|
|
* **compression-level** (`int`) -- Compression level. Must be a
|
|
number between -1 and 9 inclusive. Choose -1 for System Default.
|
|
(default -1)
|
|
:arg bool use-update: If true, Jenkins will use 'cvs update' whenever
|
|
possible for builds. This makes a build faster. But this also causes the
|
|
artifacts from the previous build to remain in the file system when a
|
|
new build starts, making it not a true clean build. (default true)
|
|
:arg bool prune-empty: Remove empty directories after checkout using the
|
|
CVS '-P' option. (default true)
|
|
:arg bool skip-changelog: Prevent the changelog being generated after
|
|
checkout has completed. (default false)
|
|
:arg bool show-all-output: Instructs CVS to show all logging output. CVS
|
|
normally runs in quiet mode but this option disables that.
|
|
(default false)
|
|
:arg bool clean-checkout: Perform clean checkout on failed update.
|
|
(default false)
|
|
:arg bool clean-copy: Force clean copy for locally modified files.
|
|
(default false)
|
|
|
|
Example
|
|
|
|
.. literalinclude:: /../../tests/scm/fixtures/cvs001.yaml
|
|
:language: yaml
|
|
.. literalinclude:: /../../tests/scm/fixtures/cvs002.yaml
|
|
:language: yaml
|
|
"""
|
|
prefix = 'hudson.scm.'
|
|
valid_loc_types = {'HEAD': 'Head', 'TAG': 'Tag', 'BRANCH': 'Branch'}
|
|
cvs = XML.SubElement(xml_parent, 'scm', {'class': prefix + 'CVSSCM'})
|
|
repos = data.get('repos')
|
|
if not repos:
|
|
raise JenkinsJobsException("'repos' empty or missing")
|
|
repos_tag = XML.SubElement(cvs, 'repositories')
|
|
for repo in repos:
|
|
repo_tag = XML.SubElement(repos_tag, prefix + 'CvsRepository')
|
|
try:
|
|
XML.SubElement(repo_tag, 'cvsRoot').text = repo['root']
|
|
except KeyError:
|
|
raise MissingAttributeError('root')
|
|
items_tag = XML.SubElement(repo_tag, 'repositoryItems')
|
|
locations = repo.get('locations')
|
|
if not locations:
|
|
raise JenkinsJobsException("'locations' empty or missing")
|
|
for location in locations:
|
|
item_tag = XML.SubElement(items_tag, prefix + 'CvsRepositoryItem')
|
|
loc_type = location.get('type', 'HEAD')
|
|
if loc_type not in valid_loc_types:
|
|
raise InvalidAttributeError('type', loc_type, valid_loc_types)
|
|
loc_class = ('{0}CvsRepositoryLocation${1}Repository'
|
|
'Location').format(prefix, valid_loc_types[loc_type])
|
|
loc_tag = XML.SubElement(item_tag, 'location',
|
|
{'class': loc_class})
|
|
XML.SubElement(loc_tag, 'locationType').text = loc_type
|
|
if loc_type == 'TAG' or loc_type == 'BRANCH':
|
|
XML.SubElement(loc_tag, 'locationName').text = location.get(
|
|
'name', '')
|
|
XML.SubElement(loc_tag, 'useHeadIfNotFound').text = str(
|
|
location.get('use-head', False)).lower()
|
|
modules = location.get('modules')
|
|
if not modules:
|
|
raise JenkinsJobsException("'modules' empty or missing")
|
|
modules_tag = XML.SubElement(item_tag, 'modules')
|
|
for module in modules:
|
|
module_tag = XML.SubElement(modules_tag, prefix + 'CvsModule')
|
|
try:
|
|
XML.SubElement(module_tag, 'remoteName'
|
|
).text = module['remote']
|
|
except KeyError:
|
|
raise MissingAttributeError('remote')
|
|
XML.SubElement(module_tag, 'localName').text = module.get(
|
|
'local-name', '')
|
|
excluded = repo.get('excluded-regions', [])
|
|
excluded_tag = XML.SubElement(repo_tag, 'excludedRegions')
|
|
for pattern in excluded:
|
|
pattern_tag = XML.SubElement(excluded_tag,
|
|
prefix + 'ExcludedRegion')
|
|
XML.SubElement(pattern_tag, 'pattern').text = pattern
|
|
compression_level = repo.get('compression-level', '-1')
|
|
if int(compression_level) not in range(-1, 10):
|
|
raise InvalidAttributeError('compression-level',
|
|
compression_level, range(-1, 10))
|
|
XML.SubElement(repo_tag, 'compressionLevel').text = compression_level
|
|
mappings = [
|
|
('use-update', 'canUseUpdate', True),
|
|
('prune-empty', 'pruneEmptyDirectories', True),
|
|
('skip-changelog', 'skipChangeLog', False),
|
|
('show-all-output', 'disableCvsQuiet', False),
|
|
('clean-checkout', 'cleanOnFailedUpdate', False),
|
|
('clean-copy', 'forceCleanCopy', False)]
|
|
convert_mapping_to_xml(cvs, data, mappings, fail_required=True)
|
|
|
|
|
|
def repo(registry, xml_parent, data):
|
|
"""yaml: repo
|
|
Specifies the repo SCM repository for this job.
|
|
Requires the Jenkins :jenkins-wiki:`Repo Plugin <Repo+Plugin>`.
|
|
|
|
:arg str manifest-url: URL of the repo manifest (required)
|
|
:arg str manifest-branch: The branch of the manifest to use (optional)
|
|
:arg str manifest-file: Initial manifest file to use when initialising
|
|
(optional)
|
|
:arg str manifest-group: Only retrieve those projects in the manifest
|
|
tagged with the provided group name (optional)
|
|
:arg list(str) ignore-projects: a list of projects in which changes would
|
|
not be considered to trigger a build when pooling (optional)
|
|
:arg str destination-dir: Location relative to the workspace root to clone
|
|
under (optional)
|
|
:arg str repo-url: custom url to retrieve the repo application (optional)
|
|
:arg str mirror-dir: Path to mirror directory to reference when
|
|
initialising (optional)
|
|
:arg int jobs: Number of projects to fetch simultaneously (default 0)
|
|
:arg int depth: Specify the depth in history to sync from the source. The
|
|
default is to sync all of the history. Use 1 to just sync the most
|
|
recent commit (default 0)
|
|
:arg bool current-branch: Fetch only the current branch from the server
|
|
(default true)
|
|
:arg bool reset-first: Remove any commits that are not on the repositories
|
|
by running the following command before anything else (default false):
|
|
``repo forall -c "git reset --hard"``
|
|
:arg bool quiet: Make repo more quiet
|
|
(default true)
|
|
:arg bool force-sync: Continue sync even if a project fails to sync
|
|
(default false)
|
|
:arg bool no-tags: Don't fetch tags (default false)
|
|
:arg bool trace: Trace git command execution into the build logs. (default
|
|
false)
|
|
:arg bool show-all-changes: When this is checked --first-parent is no
|
|
longer passed to git log when determining changesets (default false)
|
|
:arg str local-manifest: Contents of .repo/local_manifest.xml, written
|
|
prior to calling sync (optional)
|
|
|
|
Example:
|
|
|
|
.. literalinclude:: /../../tests/scm/fixtures/repo001.yaml
|
|
"""
|
|
|
|
scm = XML.SubElement(xml_parent,
|
|
'scm', {'class': 'hudson.plugins.repo.RepoScm'})
|
|
|
|
mapping = [
|
|
# option, xml name, default value
|
|
('manifest-url', 'manifestRepositoryUrl', None),
|
|
('jobs', 'jobs', 0),
|
|
('depth', 'depth', 0),
|
|
('current-branch', 'currentBranch', True),
|
|
('reset-first', 'resetFirst', False),
|
|
('quiet', 'quiet', True),
|
|
('force-sync', 'forceSync', False),
|
|
('no-tags', 'noTags', False),
|
|
('trace', 'trace', False),
|
|
('show-all-changes', 'showAllChanges', False),
|
|
]
|
|
convert_mapping_to_xml(scm, data, mapping, fail_required=True)
|
|
|
|
optional_mapping = [
|
|
# option, xml name, default value
|
|
('manifest-branch', 'manifestBranch', None),
|
|
('manifest-file', 'manifestFile', None),
|
|
('manifest-group', 'manifestGroup', None),
|
|
('destination-dir', 'destinationDir', None),
|
|
('repo-url', 'repoUrl', None),
|
|
('mirror-dir', 'mirrorDir', None),
|
|
('local-manifest', 'localManifest', None),
|
|
]
|
|
convert_mapping_to_xml(scm, data, optional_mapping, fail_required=False)
|
|
|
|
# ignore-projects does not follow the same pattern of the other parameters,
|
|
# so process it here:
|
|
ip = XML.SubElement(scm, 'ignoreProjects', {'class': 'linked-hash-set'})
|
|
ignored_projects = data.get('ignore-projects', [''])
|
|
for ignored_project in ignored_projects:
|
|
XML.SubElement(ip, 'string').text = str(ignored_project)
|
|
|
|
|
|
def store(registry, xml_parent, data):
|
|
"""yaml: store
|
|
Specifies the Visualworks Smalltalk Store repository for this job.
|
|
Requires the Jenkins :jenkins-wiki:`Visualworks Smalltalk Store Plugin
|
|
<Visualworks+Smalltalk+Store+Plugin>`.
|
|
|
|
:arg str script: name of the Store script to run
|
|
:arg str repository: name of the Store repository
|
|
:arg str version-regex: regular expression that specifies which pundle
|
|
versions should be considered (optional)
|
|
:arg str minimum-blessing: minimum blessing level to consider (optional)
|
|
:arg str parcel-builder-file: name of the file to generate as input to
|
|
a later parcel building step (optional - if not specified, then no
|
|
parcel builder file will be generated)
|
|
:arg list pundles:
|
|
|
|
:(package or bundle): (`dict`): A package or bundle to check
|
|
|
|
Example:
|
|
|
|
.. literalinclude:: /../../tests/scm/fixtures/store001.yaml
|
|
"""
|
|
namespace = 'org.jenkinsci.plugins.visualworks_store'
|
|
scm = XML.SubElement(xml_parent, 'scm',
|
|
{'class': '{0}.StoreSCM'.format(namespace)})
|
|
if 'script' in data:
|
|
XML.SubElement(scm, 'scriptName').text = data['script']
|
|
else:
|
|
raise JenkinsJobsException("Must specify a script name")
|
|
if 'repository' in data:
|
|
XML.SubElement(scm, 'repositoryName').text = data['repository']
|
|
else:
|
|
raise JenkinsJobsException("Must specify a repository name")
|
|
pundle_specs = data.get('pundles', [])
|
|
if not pundle_specs:
|
|
raise JenkinsJobsException("At least one pundle must be specified")
|
|
valid_pundle_types = ['package', 'bundle']
|
|
pundles = XML.SubElement(scm, 'pundles')
|
|
for pundle_spec in pundle_specs:
|
|
pundle = XML.SubElement(pundles, '{0}.PundleSpec'.format(namespace))
|
|
pundle_type = next(iter(pundle_spec))
|
|
pundle_name = pundle_spec[pundle_type]
|
|
if pundle_type not in valid_pundle_types:
|
|
raise JenkinsJobsException(
|
|
'pundle type must be must be one of: '
|
|
+ ', '.join(valid_pundle_types))
|
|
else:
|
|
XML.SubElement(pundle, 'name').text = pundle_name
|
|
XML.SubElement(pundle, 'pundleType').text = pundle_type.upper()
|
|
if 'version-regex' in data:
|
|
XML.SubElement(scm, 'versionRegex').text = data['version-regex']
|
|
if 'minimum-blessing' in data:
|
|
XML.SubElement(scm, 'minimumBlessingLevel').text = \
|
|
data['minimum-blessing']
|
|
if 'parcel-builder-file' in data:
|
|
XML.SubElement(scm, 'generateParcelBuilderInputFile').text = 'true'
|
|
XML.SubElement(scm, 'parcelBuilderInputFilename').text = \
|
|
data['parcel-builder-file']
|
|
else:
|
|
XML.SubElement(scm, 'generateParcelBuilderInputFile').text = 'false'
|
|
|
|
|
|
def svn(registry, xml_parent, data):
|
|
"""yaml: svn
|
|
Specifies the svn SCM repository for this job.
|
|
|
|
:arg str url: URL of the svn repository
|
|
:arg str basedir: location relative to the workspace root to checkout to
|
|
(default '.')
|
|
:arg str credentials-id: optional argument to specify the ID of credentials
|
|
to use
|
|
:arg str repo-depth: Repository depth. Can be one of 'infinity', 'empty',
|
|
'files', 'immediates' or 'unknown'. (default 'infinity')
|
|
:arg bool ignore-externals: Ignore Externals. (default false)
|
|
:arg str workspaceupdater: optional argument to specify
|
|
:arg str workspaceupdater: optional argument to specify how to update the
|
|
workspace (default wipeworkspace)
|
|
|
|
:supported values:
|
|
* **wipeworkspace** - deletes the workspace before checking out
|
|
* **revertupdate** - do an svn revert then an svn update
|
|
* **emulateclean** - delete unversioned/ignored files then update
|
|
* **update** - do an svn update as much as possible
|
|
|
|
:arg list(str) excluded-users: list of users to ignore revisions from
|
|
when polling for changes (if polling is enabled; parameter is optional)
|
|
:arg list(str) included-regions: list of file/folders to include
|
|
(optional)
|
|
:arg list(str) excluded-regions: list of file/folders to exclude (optional)
|
|
:arg list(str) excluded-commit-messages: list of commit messages to exclude
|
|
(optional)
|
|
:arg str exclusion-revprop-name: revision svn-property to ignore (optional)
|
|
:arg bool ignore-property-changes-on-directories: ignore svn-property only
|
|
changes of directories (default false)
|
|
:arg bool filter-changelog: If set Jenkins will apply the same inclusion
|
|
and exclusion patterns for displaying changelog entries as it does for
|
|
polling for changes (default false)
|
|
:arg list repos: list of repositories to checkout (optional)
|
|
:arg str viewvc-url: URL of the svn web interface (optional)
|
|
|
|
:Repo:
|
|
* **url** (`str`) -- URL for the repository
|
|
* **basedir** (`str`) -- Location relative to the workspace root
|
|
to checkout to (default '.')
|
|
* **credentials-id** - optional ID of credentials to use
|
|
* **repo-depth** - Repository depth. Can be one of 'infinity',
|
|
'empty', 'files', 'immediates' or 'unknown'. (default 'infinity')
|
|
* **ignore-externals** - Ignore Externals. (default false)
|
|
|
|
Multiple repos example:
|
|
|
|
.. literalinclude:: /../../tests/scm/fixtures/svn-multiple-repos-001.yaml
|
|
|
|
Advanced commit filtering example:
|
|
|
|
.. literalinclude:: /../../tests/scm/fixtures/svn-regions-001.yaml
|
|
"""
|
|
scm = XML.SubElement(xml_parent, 'scm', {'class':
|
|
'hudson.scm.SubversionSCM'})
|
|
if 'viewvc-url' in data:
|
|
browser = XML.SubElement(
|
|
scm, 'browser', {'class': 'hudson.scm.browsers.ViewSVN'})
|
|
XML.SubElement(browser, 'url').text = data['viewvc-url']
|
|
locations = XML.SubElement(scm, 'locations')
|
|
|
|
def populate_repo_xml(parent, data):
|
|
module = XML.SubElement(parent,
|
|
'hudson.scm.SubversionSCM_-ModuleLocation')
|
|
XML.SubElement(module, 'remote').text = data['url']
|
|
XML.SubElement(module, 'local').text = data.get('basedir', '.')
|
|
if 'credentials-id' in data:
|
|
XML.SubElement(module, 'credentialsId').text = data[
|
|
'credentials-id']
|
|
repo_depths = ['infinity', 'empty', 'files', 'immediates', 'unknown']
|
|
repo_depth = data.get('repo-depth', 'infinity')
|
|
if repo_depth not in repo_depths:
|
|
raise InvalidAttributeError('repo_depth', repo_depth, repo_depths)
|
|
XML.SubElement(module, 'depthOption').text = repo_depth
|
|
XML.SubElement(module, 'ignoreExternalsOption').text = str(
|
|
data.get('ignore-externals', False)).lower()
|
|
|
|
if 'repos' in data:
|
|
repos = data['repos']
|
|
for repo in repos:
|
|
populate_repo_xml(locations, repo)
|
|
elif 'url' in data:
|
|
populate_repo_xml(locations, data)
|
|
else:
|
|
raise JenkinsJobsException("A top level url or repos list must exist")
|
|
updater = data.get('workspaceupdater', 'wipeworkspace')
|
|
if updater == 'wipeworkspace':
|
|
updaterclass = 'CheckoutUpdater'
|
|
elif updater == 'revertupdate':
|
|
updaterclass = 'UpdateWithRevertUpdater'
|
|
elif updater == 'emulateclean':
|
|
updaterclass = 'UpdateWithCleanUpdater'
|
|
elif updater == 'update':
|
|
updaterclass = 'UpdateUpdater'
|
|
XML.SubElement(scm, 'workspaceUpdater', {'class':
|
|
'hudson.scm.subversion.' + updaterclass})
|
|
|
|
mapping = [
|
|
# option, xml name, default value
|
|
("excluded-regions", 'excludedRegions', []),
|
|
("included-regions", 'includedRegions', []),
|
|
("excluded-users", 'excludedUsers', []),
|
|
("exclusion-revprop-name", 'excludedRevprop', ''),
|
|
("excluded-commit-messages", 'excludedCommitMessages', []),
|
|
("ignore-property-changes-on-directories", 'ignoreDirPropChanges',
|
|
False),
|
|
("filter-changelog", 'filterChangelog', False),
|
|
]
|
|
|
|
for optname, xmlname, defvalue in mapping:
|
|
if isinstance(defvalue, list):
|
|
val = '\n'.join(data.get(optname, defvalue))
|
|
else:
|
|
val = data.get(optname, defvalue)
|
|
# Skip adding xml entry if default is empty and no value given
|
|
if not val and (defvalue in ['', []]):
|
|
continue
|
|
|
|
xe = XML.SubElement(scm, xmlname)
|
|
if isinstance(defvalue, bool):
|
|
xe.text = str(val).lower()
|
|
else:
|
|
xe.text = str(val)
|
|
|
|
|
|
def tfs(registry, xml_parent, data):
|
|
"""yaml: tfs
|
|
Specifies the Team Foundation Server repository for this job.
|
|
Requires the Jenkins :jenkins-wiki:`Team Foundation Server Plugin
|
|
<Team+Foundation+Server+Plugin>`.
|
|
|
|
**NOTE**: TFS Password must be entered manually on the project if a
|
|
user name is specified. The password will be overwritten with an empty
|
|
value every time the job is rebuilt with Jenkins Job Builder.
|
|
|
|
:arg str server-url: The name or URL of the team foundation server.
|
|
If the server has been registered on the machine then it is only
|
|
necessary to enter the name.
|
|
:arg str project-path: The name of the project as it is registered on the
|
|
server.
|
|
:arg str login: The user name that is registered on the server. The user
|
|
name must contain the name and the domain name. Entered as
|
|
domain\\\\user or user\@domain (optional).
|
|
**NOTE**: You must enter in at least two slashes for the
|
|
domain\\\\user format in JJB YAML. It will be rendered normally.
|
|
:arg str use-update: If true, Hudson will not delete the workspace at end
|
|
of each build. This causes the artifacts from the previous build to
|
|
remain when a new build starts. (default true)
|
|
:arg str local-path: The folder where all files will be retrieved into.
|
|
The folder name is a relative path, under the workspace of the current
|
|
job. (default .)
|
|
:arg str workspace: The name of the workspace under which the source
|
|
should be retrieved. This workspace is created at the start of a
|
|
download, and deleted at the end. You can normally omit the property
|
|
unless you want to name a workspace to avoid conflicts on the server
|
|
(i.e. when you have multiple projects on one server talking to a
|
|
Team Foundation Server). (default Hudson-${JOB_NAME}-${NODE_NAME})
|
|
|
|
The TFS plugin supports the following macros that are replaced in the
|
|
workspace name:
|
|
|
|
* ${JOB_NAME} - The name of the job.
|
|
* ${USER_NAME} - The user name that the Hudson server or slave is
|
|
running as.
|
|
* ${NODE_NAME} - The name of the node/slave that the plugin currently
|
|
is executed on. Note that this is not the hostname, this value is
|
|
the Hudson configured name of the slave/node.
|
|
* ${ENV} - The environment variable that is set on the master or slave.
|
|
|
|
|
|
:arg dict web-access: Adds links in "changes" views within Jenkins to an
|
|
external system for browsing the details of those changes. The "Auto"
|
|
selection attempts to infer the repository browser from other jobs,
|
|
if supported by the SCM and a job with matching SCM details can be
|
|
found. (optional, default Auto).
|
|
|
|
:web-access value:
|
|
* **web-url** -- Enter the URL to the TSWA server. The plugin will
|
|
strip the last path (if any) of the URL when building URLs for
|
|
change set pages and other pages. (optional, default
|
|
uses server-url)
|
|
|
|
|
|
Examples:
|
|
|
|
.. literalinclude:: /../../tests/scm/fixtures/tfs-001.yaml
|
|
|
|
.. literalinclude:: /../../tests/scm/fixtures/tfs-002.yaml
|
|
|
|
"""
|
|
|
|
tfs = XML.SubElement(xml_parent, 'scm',
|
|
{'class': 'hudson.plugins.tfs.'
|
|
'TeamFoundationServerScm'})
|
|
XML.SubElement(tfs, 'serverUrl').text = str(
|
|
data.get('server-url', ''))
|
|
XML.SubElement(tfs, 'projectPath').text = str(
|
|
data.get('project-path', ''))
|
|
XML.SubElement(tfs, 'localPath').text = str(
|
|
data.get('local-path', '.'))
|
|
XML.SubElement(tfs, 'workspaceName').text = str(
|
|
data.get('workspace', 'Hudson-${JOB_NAME}-${NODE_NAME}'))
|
|
# TODO: In the future, it would be nice to have a place that can pull
|
|
# passwords into JJB without having to commit them in plaintext. This
|
|
# could also integrate nicely with global configuration options.
|
|
XML.SubElement(tfs, 'userPassword')
|
|
XML.SubElement(tfs, 'userName').text = str(
|
|
data.get('login', ''))
|
|
XML.SubElement(tfs, 'useUpdate').text = str(
|
|
data.get('use-update', True))
|
|
store = data.get('web-access', None)
|
|
if 'web-access' in data and isinstance(store, list):
|
|
web = XML.SubElement(tfs, 'repositoryBrowser',
|
|
{'class': 'hudson.plugins.tfs.browsers.'
|
|
'TeamSystemWebAccessBrowser'})
|
|
XML.SubElement(web, 'url').text = str(store[0].get('web-url', None))
|
|
elif 'web-access' in data and store is None:
|
|
XML.SubElement(tfs, 'repositoryBrowser', {'class': 'hudson.'
|
|
'plugins.tfs.browsers.'
|
|
'TeamSystemWebAccess'
|
|
'Browser'})
|
|
|
|
|
|
def workspace(registry, xml_parent, data):
|
|
"""yaml: workspace
|
|
Specifies the cloned workspace for this job to use as a SCM source.
|
|
Requires the Jenkins :jenkins-wiki:`Clone Workspace SCM Plugin
|
|
<Clone+Workspace+SCM+Plugin>`.
|
|
|
|
The job the workspace is cloned from must be configured with an
|
|
clone-workspace publisher
|
|
|
|
:arg str parent-job: The name of the parent job to clone the
|
|
workspace from.
|
|
:arg str criteria: Set the criteria to determine what build of the parent
|
|
project to use. Can be one of 'Any', 'Not Failed' or 'Successful'.
|
|
(default Any)
|
|
|
|
|
|
Example:
|
|
|
|
.. literalinclude:: /../../tests/scm/fixtures/workspace001.yaml
|
|
"""
|
|
|
|
workspace = XML.SubElement(xml_parent, 'scm', {'class': 'hudson.plugins.'
|
|
'cloneworkspace.CloneWorkspaceSCM'})
|
|
XML.SubElement(workspace, 'parentJobName').text = str(
|
|
data.get('parent-job', ''))
|
|
|
|
criteria_list = ['Any', 'Not Failed', 'Successful']
|
|
|
|
criteria = data.get('criteria', 'Any').title()
|
|
|
|
if 'criteria' in data and criteria not in criteria_list:
|
|
raise JenkinsJobsException(
|
|
'clone-workspace criteria must be one of: '
|
|
+ ', '.join(criteria_list))
|
|
else:
|
|
XML.SubElement(workspace, 'criteria').text = criteria
|
|
|
|
|
|
def hg(self, xml_parent, data):
|
|
"""yaml: hg
|
|
Specifies the mercurial SCM repository for this job.
|
|
Requires the Jenkins :jenkins-wiki:`Mercurial Plugin <Mercurial+Plugin>`.
|
|
|
|
:arg str url: URL of the hg repository
|
|
:arg str credentials-id: ID of credentials to use to connect (optional)
|
|
:arg str revision-type: revision type to use (default 'branch')
|
|
:arg str revision: the branch or tag name you would like to track
|
|
(default 'default')
|
|
:arg list(str) modules: reduce unnecessary builds by specifying a list of
|
|
"modules" within the repository. A module is a directory name within
|
|
the repository that this project lives in. (default '')
|
|
:arg bool clean: wipe any local modifications or untracked files in the
|
|
repository checkout (default false)
|
|
:arg str subdir: check out the Mercurial repository into this
|
|
subdirectory of the job's workspace (optional)
|
|
:arg bool disable-changelog: do not calculate the Mercurial changelog
|
|
for each build (default false)
|
|
:arg str browser: what repository browser to use
|
|
|
|
:browsers supported:
|
|
* **auto** - (default)
|
|
* **bitbucketweb** - https://bitbucket.org/
|
|
* **fisheye** - https://www.atlassian.com/software/fisheye
|
|
* **googlecode** - https://code.google.com/
|
|
* **hgweb** - https://www.selenic.com/hg/help/hgweb
|
|
* **kilnhg** - https://www.fogcreek.com/kiln/
|
|
* **rhodecode** - https://rhodecode.com/ (versions >= 1.2)
|
|
* **rhodecode-pre-1.2.0** - https://rhodecode.com/ (versions < 1.2)
|
|
|
|
:arg str browser-url: url for the repository browser
|
|
(required if browser is set)
|
|
|
|
|
|
Example:
|
|
|
|
.. literalinclude:: ../../tests/scm/fixtures/hg02.yaml
|
|
"""
|
|
scm = XML.SubElement(xml_parent, 'scm', {'class':
|
|
'hudson.plugins.mercurial.MercurialSCM'})
|
|
if 'url' in data:
|
|
XML.SubElement(scm, 'source').text = data['url']
|
|
else:
|
|
raise JenkinsJobsException("A top level url must exist")
|
|
|
|
if 'credentials-id' in data:
|
|
XML.SubElement(scm, 'credentialsId').text = data['credentials-id']
|
|
|
|
revision_type_dict = {
|
|
'branch': 'BRANCH',
|
|
'tag': 'TAG',
|
|
}
|
|
try:
|
|
revision_type = revision_type_dict[data.get('revision-type', 'branch')]
|
|
except KeyError:
|
|
raise JenkinsJobsException('Invalid revision-type %r' %
|
|
data.get('revision-type'))
|
|
XML.SubElement(scm, 'revisionType').text = revision_type
|
|
|
|
XML.SubElement(scm, 'revision').text = data.get('revision', 'default')
|
|
|
|
if 'subdir' in data:
|
|
XML.SubElement(scm, 'subdir').text = data['subdir']
|
|
|
|
xc = XML.SubElement(scm, 'clean')
|
|
xc.text = str(data.get('clean', False)).lower()
|
|
|
|
modules = data.get('modules', '')
|
|
if isinstance(modules, list):
|
|
modules = " ".join(modules)
|
|
XML.SubElement(scm, 'modules').text = modules
|
|
|
|
xd = XML.SubElement(scm, 'disableChangeLog')
|
|
xd.text = str(data.get('disable-changelog', False)).lower()
|
|
|
|
browser = data.get('browser', 'auto')
|
|
browserdict = {
|
|
'auto': '',
|
|
'bitbucket': 'BitBucket', # deprecated
|
|
'bitbucketweb': 'BitBucket',
|
|
'fisheye': 'FishEye',
|
|
'googlecode': 'GoogleCode',
|
|
'hgweb': 'HgWeb',
|
|
'kilnhg': 'KilnHG',
|
|
'rhodecode': 'RhodeCode',
|
|
'rhodecode-pre-1.2.0': 'RhodeCodeLegacy'
|
|
}
|
|
|
|
if browser not in browserdict:
|
|
raise JenkinsJobsException("Browser entered is not valid must be one "
|
|
"of: %s" % ", ".join(browserdict.keys()))
|
|
if browser != 'auto':
|
|
bc = XML.SubElement(scm, 'browser',
|
|
{'class': 'hudson.plugins.mercurial.browser.' +
|
|
browserdict[browser]})
|
|
if 'browser-url' in data:
|
|
XML.SubElement(bc, 'url').text = data['browser-url']
|
|
else:
|
|
raise JenkinsJobsException("A browser-url must be specified along "
|
|
"with browser.")
|
|
|
|
|
|
def openshift_img_streams(registry, xml_parent, data):
|
|
"""yaml: openshift-img-streams
|
|
Rather than a Build step extension plugin, this is an extension of the
|
|
Jenkins SCM plugin, where this baked-in polling mechanism provided by
|
|
Jenkins is leveraged by exposing some of the common semantics between
|
|
OpenShift ImageStreams (which are abstractions of Docker repositories)
|
|
and SCMs - versions / commit IDs of related artifacts
|
|
(images vs. programmatics files)
|
|
Requires the Jenkins :jenkins-wiki:`OpenShift
|
|
Pipeline Plugin <OpenShift+Pipeline+Plugin>`._
|
|
|
|
:arg str image-stream-name: The name of the ImageStream is what shows up
|
|
in the NAME column if you dump all the ImageStream's with the
|
|
`oc get is` command invocation. (default nodejs-010-centos7)
|
|
:arg str tag: The specific image tag within the ImageStream to monitor.
|
|
(default latest)
|
|
:arg str api-url: This would be the value you specify if you leverage the
|
|
--server option on the OpenShift `oc` command.
|
|
(default \https://openshift.default.svc.cluster.local\)
|
|
:arg str namespace: The value here should be whatever was the output
|
|
form `oc project` when you created the BuildConfig you want to run
|
|
a Build on. (default test)
|
|
:arg str auth-token: The value here is what you supply with the --token
|
|
option when invoking the OpenShift `oc` command. (default '')
|
|
:arg bool verbose: This flag is the toggle for
|
|
turning on or off detailed logging in this plug-in. (default false)
|
|
|
|
Full Example:
|
|
|
|
.. literalinclude::
|
|
../../tests/scm/fixtures/openshift-img-streams001.yaml
|
|
:language: yaml
|
|
|
|
Minimal Example:
|
|
|
|
.. literalinclude::
|
|
../../tests/scm/fixtures/openshift-img-streams002.yaml
|
|
:language: yaml
|
|
"""
|
|
scm = XML.SubElement(xml_parent,
|
|
'scm', {'class':
|
|
'com.openshift.jenkins.plugins.pipeline.'
|
|
'OpenShiftImageStreams'})
|
|
mapping = [
|
|
# option, xml name, default value
|
|
("image-stream-name", 'imageStreamName', 'nodejs-010-centos7'),
|
|
("tag", 'tag', 'latest'),
|
|
("api-url", 'apiURL', 'https://openshift.default.svc.cluster.local'),
|
|
("namespace", 'namespace', 'test'),
|
|
("auth-token", 'authToken', ''),
|
|
("verbose", 'verbose', False),
|
|
]
|
|
convert_mapping_to_xml(scm, data, mapping, fail_required=True)
|
|
|
|
|
|
def bzr(registry, xml_parent, data):
|
|
"""yaml: bzr
|
|
Specifies the bzr SCM repository for this job.
|
|
Requires the Jenkins :jenkins-wiki:`Bazaar Plugin <Bazaar+Plugin>`.
|
|
|
|
:arg str url: URL of the bzr branch (required)
|
|
:arg bool clean-tree: Clean up the workspace (using bzr) before pulling
|
|
the branch (default false)
|
|
:arg bool lightweight-checkout: Use a lightweight checkout instead of a
|
|
full branch (default false)
|
|
:arg str browser: The repository browser to use.
|
|
|
|
:browsers supported:
|
|
* **auto** - (default)
|
|
* **loggerhead** - as used by Launchpad
|
|
* **opengrok** - https://opengrok.github.io/OpenGrok/
|
|
|
|
:arg str browser-url:
|
|
URL for the repository browser (required if browser is set).
|
|
|
|
:arg str opengrok-root-module:
|
|
Root module for OpenGrok (required if browser is opengrok).
|
|
|
|
Example:
|
|
|
|
.. literalinclude:: /../../tests/scm/fixtures/bzr001.yaml
|
|
:language: yaml
|
|
"""
|
|
mapping = [
|
|
# option, xml name, default value (text), attributes (hard coded)
|
|
('url', 'source', None),
|
|
('clean-tree', 'cleantree', False),
|
|
('lightweight-checkout', 'checkout', False),
|
|
]
|
|
scm_element = XML.SubElement(
|
|
xml_parent, 'scm', {'class': 'hudson.plugins.bazaar.BazaarSCM'})
|
|
convert_mapping_to_xml(scm_element, data, mapping, fail_required=True)
|
|
|
|
browser_name_to_class = {
|
|
'loggerhead': 'Loggerhead',
|
|
'opengrok': 'OpenGrok',
|
|
}
|
|
browser = data.get('browser', 'auto')
|
|
if browser == 'auto':
|
|
return
|
|
if browser not in browser_name_to_class:
|
|
raise InvalidAttributeError('browser', browser,
|
|
browser_name_to_class.keys())
|
|
browser_element = XML.SubElement(
|
|
scm_element,
|
|
'browser',
|
|
{'class': 'hudson.plugins.bazaar.browsers.{0}'.format(
|
|
browser_name_to_class[browser])})
|
|
XML.SubElement(browser_element, 'url').text = data['browser-url']
|
|
if browser == 'opengrok':
|
|
XML.SubElement(browser_element, 'rootModule').text = (
|
|
data['opengrok-root-module'])
|
|
|
|
|
|
def url(registry, xml_parent, data):
|
|
"""yaml: url
|
|
|
|
Watch for changes in, and download an artifact from a particular url.
|
|
Requires the Jenkins :jenkins-wiki:`URL SCM <URL+SCM>`.
|
|
|
|
:arg list url-list: List of URLs to watch. (required)
|
|
:arg bool clear-workspace: If set to true, clear the workspace before
|
|
downloading the artifact(s) specified in url-list. (default false)
|
|
|
|
Examples:
|
|
|
|
.. literalinclude:: ../../tests/scm/fixtures/url001.yaml
|
|
:language: yaml
|
|
.. literalinclude:: ../../tests/scm/fixtures/url002.yaml
|
|
:language: yaml
|
|
"""
|
|
|
|
scm = XML.SubElement(xml_parent, 'scm', {'class':
|
|
'hudson.plugins.URLSCM.URLSCM'})
|
|
urls = XML.SubElement(scm, 'urls')
|
|
try:
|
|
for data_url in data['url-list']:
|
|
url_tuple = XML.SubElement(
|
|
urls, 'hudson.plugins.URLSCM.URLSCM_-URLTuple')
|
|
XML.SubElement(url_tuple, 'urlString').text = data_url
|
|
except KeyError as e:
|
|
raise MissingAttributeError(e.args[0])
|
|
XML.SubElement(scm, 'clearWorkspace').text = str(
|
|
data.get('clear-workspace', False)).lower()
|
|
|
|
|
|
def dimensions(registry, xml_parent, data):
|
|
"""yaml: dimensions
|
|
|
|
Specifies the Dimensions SCM repository for this job.
|
|
Requires Jenkins :jenkins-wiki:`Dimensions Plugin <Dimensions+Plugin>`.
|
|
|
|
:arg str project: Project name of format PRODUCT_ID:PROJECT_NAME (required)
|
|
:arg str permissions: Default Permissions for updated files
|
|
(default: DEFAULT)
|
|
|
|
:Permissions:
|
|
* **DEFAULT**
|
|
* **READONLY**
|
|
* **WRITABLE**
|
|
:arg str eol: End of line (default: DEFAULT)
|
|
|
|
:End of line:
|
|
* **DEFAULT**
|
|
* **UNIX**
|
|
* **WINDOWS**
|
|
* **UNCHANGED**
|
|
:arg list folders: Folders to monitor (default /)
|
|
:arg list exclude: Paths to exclude from monitor
|
|
:arg str username: Repository username for this job
|
|
:arg str password: Repository password for this job
|
|
:arg str server: Dimensions server for this job
|
|
:arg str database: Dimensions database for this job.
|
|
Format must be database@dsn
|
|
:arg bool update: Use update (default false)
|
|
:arg bool clear-workspace: Clear workspace prior to build (default false)
|
|
:arg bool force-build: Force build even if the repository SCM checkout
|
|
operation fails (default false)
|
|
:arg bool overwrite-modified: Overwrite files in worspace from
|
|
repository files (default false)
|
|
:arg bool expand-vars: Expand substitution variables (default false)
|
|
:arg bool no-metadata: Checkout files with no metadata (default false)
|
|
:arg bool maintain-timestamp: Maintain file timestamp from Dimensions
|
|
(default false)
|
|
:arg bool slave-checkout: Force slave based checkout (default false)
|
|
:arg str timezone: Server timezone
|
|
:arg str web-url: Dimensions Web URL
|
|
|
|
Examples:
|
|
|
|
.. literalinclude:: /../../tests/scm/fixtures/dimensions-minimal.yaml
|
|
:language: yaml
|
|
.. literalinclude:: /../../tests/scm/fixtures/dimensions-full.yaml
|
|
:language: yaml
|
|
|
|
"""
|
|
|
|
scm = XML.SubElement(
|
|
xml_parent,
|
|
'scm', {'class': 'hudson.plugins.dimensionsscm.DimensionsSCM'})
|
|
|
|
# List to check against for valid permission
|
|
perm = ['DEFAULT', 'READONLY', 'WRITABLE']
|
|
|
|
# List to check against for valid end of line
|
|
eol = ['DEFAULT', 'UNIX', 'WINDOWS', 'UNCHANGED']
|
|
|
|
mapping = [
|
|
# option, xml name, default value (text), attributes (hard coded)
|
|
('project', 'project', None),
|
|
('permissions', 'permissions', 'DEFAULT', perm),
|
|
('eol', 'eol', 'DEFAULT', eol),
|
|
('update', 'canJobUpdate', False),
|
|
('clear-workspace', 'canJobDelete', False),
|
|
('force-build', 'canJobForce', False),
|
|
('overwrite-modified', 'canJobRevert', False),
|
|
('expand-vars', 'canJobExpand', False),
|
|
('no-metadata', 'canJobNoMetadata', False),
|
|
('maintain-timestamp', 'canJobNoTouch', False),
|
|
('slave-checkout', 'forceAsSlave', False),
|
|
]
|
|
convert_mapping_to_xml(scm, data, mapping, fail_required=True)
|
|
|
|
# Folders to monitor. Default '/'
|
|
folders = XML.SubElement(scm, 'folders')
|
|
if 'folders' in data:
|
|
for folder in data['folders']:
|
|
XML.SubElement(folders, 'string').text = folder
|
|
else:
|
|
XML.SubElement(folders, 'string').text = '/'
|
|
|
|
# Excluded paths
|
|
exclude = XML.SubElement(scm, 'pathsToExclude')
|
|
if 'exclude' in data:
|
|
for exc in data['exclude']:
|
|
XML.SubElement(exclude, 'string').text = exc
|
|
|
|
optional_mapping = [
|
|
# option, xml name, default value (text), attributes (hard coded)
|
|
('username', 'jobUserName', None),
|
|
('password', 'jobPasswd', None),
|
|
('server', 'jobServer', None),
|
|
('database', 'jobDatabase', None),
|
|
('timezone', 'jobTimeZone', None),
|
|
('web-url', 'jobWebUrl', None),
|
|
]
|
|
convert_mapping_to_xml(scm, data, optional_mapping, fail_required=False)
|
|
|
|
|
|
class SCM(jenkins_jobs.modules.base.Base):
|
|
sequence = 30
|
|
|
|
component_type = 'scm'
|
|
component_list_type = 'scm'
|
|
|
|
def gen_xml(self, xml_parent, data):
|
|
scms_parent = XML.Element('scms')
|
|
for scm in data.get('scm', []):
|
|
self.registry.dispatch('scm', 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)
|
|
|
|
for scms_child in scms_parent:
|
|
try:
|
|
scms_child.tag = scms_child.attrib['class']
|
|
del(scms_child.attrib['class'])
|
|
except KeyError:
|
|
pass
|
|
|
|
xml_parent.append(scms_parent)
|
|
|
|
|
|
class PipelineSCM(jenkins_jobs.modules.base.Base):
|
|
sequence = 30
|
|
|
|
component_type = 'pipeline-scm'
|
|
component_list_type = 'pipeline-scm'
|
|
|
|
def gen_xml(self, xml_parent, data):
|
|
definition_parent = xml_parent.find('definition')
|
|
pipeline_dict = data.get(self.component_type, {})
|
|
scms = pipeline_dict.get('scm')
|
|
if scms:
|
|
scms_count = len(scms)
|
|
if scms_count == 0:
|
|
raise JenkinsJobsException("'scm' missing or empty")
|
|
elif scms_count == 1:
|
|
self.registry.dispatch('scm', definition_parent, scms[0])
|
|
XML.SubElement(definition_parent, 'scriptPath'
|
|
).text = pipeline_dict.get('script-path',
|
|
'Jenkinsfile')
|
|
else:
|
|
raise JenkinsJobsException('Only one SCM can be specified '
|
|
'as pipeline-scm')
|