Tools to make Jenkins jobs from templates
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

scm.py 66KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543
  1. # Copyright 2012 Hewlett-Packard Development Company, L.P.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing permissions and limitations
  13. # under the License.
  14. """
  15. The SCM module allows you to specify the source code location for the
  16. project. It adds the ``scm`` attribute to the :ref:`Job` definition,
  17. which accepts any number of scm definitions. It is also possible to pass
  18. ``[]`` to the ``scm`` attribute. This is useful when a set of configs has a
  19. global default ``scm`` and you want to a particular job to override that
  20. default with no SCM.
  21. **Component**: scm
  22. :Macro: scm
  23. :Entry Point: jenkins_jobs.scm
  24. The scm module allows referencing multiple repositories in a Jenkins job.
  25. Note: Adding more than one scm definition requires the Jenkins
  26. :jenkins-wiki:`Multiple SCMs plugin <Multiple+SCMs+Plugin>`.
  27. Example of multiple repositories in a single job:
  28. .. literalinclude:: /../../tests/macros/fixtures/scm/multi-scms001.yaml
  29. Example of an empty ``scm``:
  30. .. literalinclude:: /../../tests/scm/fixtures/empty.yaml
  31. """
  32. import logging
  33. import xml.etree.ElementTree as XML
  34. from jenkins_jobs.errors import InvalidAttributeError
  35. from jenkins_jobs.errors import JenkinsJobsException
  36. import jenkins_jobs.modules.base
  37. import jenkins_jobs.modules.helpers as helpers
  38. def git(registry, xml_parent, data):
  39. """yaml: git
  40. Specifies the git SCM repository for this job.
  41. Requires the Jenkins :jenkins-wiki:`Git Plugin <Git+Plugin>`.
  42. :arg str url: URL of the git repository
  43. :arg str credentials-id: ID of credential to use to connect, which is the
  44. last field (a 32-digit hexadecimal code) of the path of URL visible
  45. after you clicked the credential under Jenkins Global credentials.
  46. (optional)
  47. :arg str refspec: refspec to fetch (default
  48. '+refs/heads/\*:refs/remotes/remoteName/\*')
  49. :arg str name: name to fetch (default 'origin')
  50. :arg list(str) remotes: list of remotes to set up (optional, only needed if
  51. multiple remotes need to be set up)
  52. :Remote:
  53. * **url** (`string`) - url of remote repo
  54. * **refspec** (`string`) - refspec to fetch (optional)
  55. * **credentials-id** - ID of credential to use to connect, which
  56. is the last field of the path of URL (a 32-digit hexadecimal
  57. code) visible after you clicked credential under Jenkins Global
  58. credentials. (optional)
  59. :arg list(str) branches: list of branch specifiers to build (default '**')
  60. :arg bool skip-tag: Skip tagging (default true)
  61. .. deprecated:: 2.0.0. Please use per-build-tag extension, which has
  62. the inverse meaning.
  63. :arg bool clean: Clean after checkout (default false)
  64. .. deprecated:: 1.1.1. Please use clean extension format.
  65. :arg bool fastpoll: Use fast remote polling (default false)
  66. :arg bool disable-submodules: Disable submodules (default false)
  67. .. deprecated:: 1.1.1. Please use submodule extension.
  68. :arg bool recursive-submodules: Recursively update submodules (default
  69. false)
  70. .. deprecated:: 1.1.1. Please use submodule extension.
  71. :arg str git-tool: The name of the Git installation to use (default
  72. 'Default')
  73. :arg str reference-repo: Path of the reference repo to use during clone
  74. (optional)
  75. :arg str browser: what repository browser to use.
  76. :browsers supported:
  77. * **auto** - (default)
  78. * **assemblaweb** - https://www.assembla.com/home
  79. * **bitbucketweb** - https://bitbucket.org/
  80. * **cgit** - https://git.zx2c4.com/cgit/about/
  81. * **fisheye** - https://www.atlassian.com/software/fisheye
  82. * **gitblit** - http://gitblit.com/
  83. * **githubweb** - https://github.com/
  84. * **gitiles** - https://code.google.com/p/gitiles/
  85. * **gitlab** - https://about.gitlab.com/
  86. * **gitlist** - http://gitlist.org/
  87. * **gitoriousweb** - https://gitorious.org/
  88. * **gitweb** - https://git-scm.com/docs/gitweb
  89. * **kiln** - https://www.fogcreek.com/kiln/
  90. * **microsoft\-tfs\-2013** - |tfs_2013|
  91. * **phabricator** - http://phabricator.org/
  92. * **redmineweb** - http://www.redmine.org/
  93. * **rhodecode** - https://rhodecode.com/
  94. * **stash** - https://www.atlassian.com/software/bitbucket/server
  95. * **viewgit** - http://viewgit.fealdia.org/
  96. :arg str browser-url: url for the repository browser (required if browser
  97. is not 'auto', no default)
  98. :arg str browser-version: version of the repository browser (GitLab only,
  99. default '0.0')
  100. :arg str project-name: project name in Gitblit and ViewGit repobrowser
  101. (optional)
  102. :arg str repo-name: repository name in phabricator repobrowser (optional)
  103. :arg str git-config-name: Configure name for Git clone (optional)
  104. :arg str git-config-email: Configure email for Git clone (optional)
  105. :extensions:
  106. * **basedir** (`string`) - Location relative to the workspace root to
  107. clone to (default workspace)
  108. * **changelog-against** (`dict`)
  109. * **remote** (`string`) - name of repo that contains branch to
  110. create changelog against (default 'origin')
  111. * **branch** (`string`) - name of the branch to create changelog
  112. against (default 'master')
  113. * **choosing-strategy**: (`string`) - Jenkins class for selecting what
  114. to build. Can be one of `default`,`inverse`, or `gerrit`
  115. (default 'default')
  116. * **clean** (`dict`)
  117. * **after** (`bool`) - Clean the workspace after checkout
  118. * **before** (`bool`) - Clean the workspace before checkout
  119. * **excluded-users**: (`list(string)`) - list of users to ignore
  120. revisions from when polling for changes.
  121. (if polling is enabled, optional)
  122. * **included-regions**: (`list(string)`) - list of file/folders to
  123. include (optional)
  124. * **excluded-regions**: (`list(string)`) - list of file/folders to
  125. exclude (optional)
  126. * **ignore-commits-with-messages** (`list(str)`) - Revisions committed
  127. with messages matching these patterns will be ignored. (optional)
  128. * **ignore-notify**: (`bool`) - Ignore notifyCommit URL accesses
  129. (default false)
  130. * **force-polling-using-workspace** (`bool`) - Force polling using
  131. workspace (default false)
  132. * **local-branch** (`string`) - Checkout/merge to local branch
  133. (optional)
  134. * **merge** (`dict`)
  135. * **remote** (`string`) - name of repo that contains branch to
  136. merge to (default 'origin')
  137. * **branch** (`string`) - name of the branch to merge to
  138. * **strategy** (`string`) - merge strategy. Can be one of
  139. 'default', 'resolve', 'recursive', 'octopus', 'ours',
  140. 'subtree'. (default 'default')
  141. * **fast-forward-mode** (`string`) - merge fast-forward mode.
  142. Can be one of 'FF', 'FF_ONLY' or 'NO_FF'. (default 'FF')
  143. * **per-build-tag** (`bool`) - Create a tag in the workspace for every
  144. build. (default is inverse of skip-tag if set, otherwise false)
  145. * **prune** (`bool`) - Prune remote branches (default false)
  146. * **scm-name** (`string`) - The unique scm name for this Git SCM
  147. (optional)
  148. * **shallow-clone** (`bool`) - Perform shallow clone (default false)
  149. * **depth** (`int`) - Set shallow clone depth (default 1)
  150. * **do-not-fetch-tags** (`bool`) - Perform a clone without tags
  151. (default false)
  152. * **sparse-checkout** (`dict`)
  153. * **paths** (`list`) - List of paths to sparse checkout. (optional)
  154. * **submodule** (`dict`)
  155. * **disable** (`bool`) - By disabling support for submodules you
  156. can still keep using basic git plugin functionality and just have
  157. Jenkins to ignore submodules completely as if they didn't exist.
  158. * **recursive** (`bool`) - Retrieve all submodules recursively
  159. (uses '--recursive' option which requires git>=1.6.5)
  160. * **tracking** (`bool`) - Retrieve the tip of the configured
  161. branch in .gitmodules (Uses '\-\-remote' option which requires
  162. git>=1.8.2)
  163. * **parent-credentials** (`bool`) - Use credentials from default
  164. remote of parent repository (default false).
  165. * **reference-repo** (`str`) - Path of the reference repo to use
  166. during clone (optional)
  167. * **timeout** (`int`) - Specify a timeout (in minutes) for
  168. submodules operations (default 10).
  169. * **timeout** (`str`) - Timeout for git commands in minutes (optional)
  170. * **use-author** (`bool`): Use author rather than committer in Jenkin's
  171. build changeset (default false)
  172. * **wipe-workspace** (`bool`) - Wipe out workspace before build
  173. (default true)
  174. Example:
  175. .. literalinclude:: /../../tests/scm/fixtures/git001.yaml
  176. .. |tfs_2013| replace::
  177. https://www.visualstudio.com/en-us/products/tfs-overview-vs.aspx
  178. """
  179. logger = logging.getLogger("%s:git" % __name__)
  180. # XXX somebody should write the docs for those with option name =
  181. # None so we have a sensible name/key for it.
  182. mapping = [
  183. # option, xml name, default value (text), attributes (hard coded)
  184. ("disable-submodules", 'disableSubmodules', False),
  185. ("recursive-submodules", 'recursiveSubmodules', False),
  186. (None, 'doGenerateSubmoduleConfigurations', False),
  187. # XXX is this the same as force-polling-using-workspace?
  188. ("fastpoll", 'remotePoll', False),
  189. # XXX does this option still exist?
  190. ("git-tool", 'gitTool', "Default"),
  191. (None, 'submoduleCfg', '', {'class': 'list'}),
  192. ('reference-repo', 'reference', ''),
  193. ("git-config-name", 'gitConfigName', ''),
  194. ("git-config-email", 'gitConfigEmail', ''),
  195. ]
  196. choosing_strategies = {
  197. 'default': 'hudson.plugins.git.util.DefaultBuildChooser',
  198. 'gerrit': ('com.sonyericsson.hudson.plugins.'
  199. 'gerrit.trigger.hudsontrigger.GerritTriggerBuildChooser'),
  200. 'inverse': 'hudson.plugins.git.util.InverseBuildChooser',
  201. }
  202. scm = XML.SubElement(xml_parent,
  203. 'scm', {'class': 'hudson.plugins.git.GitSCM'})
  204. XML.SubElement(scm, 'configVersion').text = '2'
  205. user = XML.SubElement(scm, 'userRemoteConfigs')
  206. if 'remotes' not in data:
  207. data['remotes'] = [{data.get('name', 'origin'): data.copy()}]
  208. for remoteData in data['remotes']:
  209. huser = XML.SubElement(user, 'hudson.plugins.git.UserRemoteConfig')
  210. remoteName = next(iter(remoteData.keys()))
  211. XML.SubElement(huser, 'name').text = remoteName
  212. remoteParams = next(iter(remoteData.values()))
  213. if 'refspec' in remoteParams:
  214. refspec = remoteParams['refspec']
  215. else:
  216. refspec = '+refs/heads/*:refs/remotes/' + remoteName + '/*'
  217. XML.SubElement(huser, 'refspec').text = refspec
  218. if 'url' in remoteParams:
  219. remoteURL = remoteParams['url']
  220. else:
  221. raise JenkinsJobsException(
  222. 'Must specify a url for git remote \"' + remoteName + '"')
  223. XML.SubElement(huser, 'url').text = remoteURL
  224. if 'credentials-id' in remoteParams:
  225. credentialsId = remoteParams['credentials-id']
  226. XML.SubElement(huser, 'credentialsId').text = credentialsId
  227. xml_branches = XML.SubElement(scm, 'branches')
  228. branches = data.get('branches', ['**'])
  229. for branch in branches:
  230. bspec = XML.SubElement(xml_branches, 'hudson.plugins.git.BranchSpec')
  231. XML.SubElement(bspec, 'name').text = branch
  232. for elem in mapping:
  233. (optname, xmlname, val) = elem[:3]
  234. # Throw warning for deprecated settings and skip if the 'submodule' key
  235. # is available.
  236. submodule_cfgs = ['disable-submodules', 'recursive-submodules']
  237. if optname in submodule_cfgs:
  238. if optname in data:
  239. logger.warning(
  240. "'{0}' is deprecated, please convert to use the "
  241. "'submodule' section instead as support for this "
  242. "top level option will be removed in a future "
  243. "release.".format(optname))
  244. if 'submodule' in data:
  245. continue
  246. attrs = {}
  247. if len(elem) >= 4:
  248. attrs = elem[3]
  249. xe = XML.SubElement(scm, xmlname, attrs)
  250. if optname and optname in data:
  251. val = data[optname]
  252. if type(val) == bool:
  253. xe.text = str(val).lower()
  254. else:
  255. xe.text = val
  256. exts_node = XML.SubElement(scm, 'extensions')
  257. impl_prefix = 'hudson.plugins.git.extensions.impl.'
  258. if 'basedir' in data:
  259. ext = XML.SubElement(exts_node,
  260. impl_prefix + 'RelativeTargetDirectory')
  261. XML.SubElement(ext, 'relativeTargetDir').text = data['basedir']
  262. if 'changelog-against' in data:
  263. ext_name = impl_prefix + 'ChangelogToBranch'
  264. ext = XML.SubElement(exts_node, ext_name)
  265. opts = XML.SubElement(ext, 'options')
  266. change_remote = data['changelog-against'].get('remote', 'origin')
  267. change_branch = data['changelog-against'].get('branch', 'master')
  268. XML.SubElement(opts, 'compareRemote').text = change_remote
  269. XML.SubElement(opts, 'compareTarget').text = change_branch
  270. if 'choosing-strategy' in data:
  271. try:
  272. choosing_strategy = choosing_strategies[
  273. data.get('choosing-strategy')]
  274. except KeyError:
  275. raise ValueError('Invalid choosing-strategy %r' %
  276. data.get('choosing-strategy'))
  277. ext = XML.SubElement(exts_node, impl_prefix + 'BuildChooserSetting')
  278. XML.SubElement(ext, 'buildChooser', {'class': choosing_strategy})
  279. if 'clean' in data:
  280. # Keep support for old format 'clean' configuration by checking
  281. # if 'clean' is boolean. Else we're using the new extensions style.
  282. if isinstance(data['clean'], bool):
  283. clean_after = data['clean']
  284. clean_before = False
  285. logger.warning(
  286. "'clean: bool' configuration format is deprecated, "
  287. "please use the extension style format to configure "
  288. "this option.")
  289. else:
  290. clean_after = data['clean'].get('after', False)
  291. clean_before = data['clean'].get('before', False)
  292. if clean_after:
  293. ext_name = impl_prefix + 'CleanCheckout'
  294. ext = XML.SubElement(exts_node, ext_name)
  295. if clean_before:
  296. ext_name = impl_prefix + 'CleanBeforeCheckout'
  297. ext = XML.SubElement(exts_node, ext_name)
  298. if 'excluded-users' in data:
  299. excluded_users = '\n'.join(data['excluded-users'])
  300. ext = XML.SubElement(exts_node, impl_prefix + 'UserExclusion')
  301. XML.SubElement(ext, 'excludedUsers').text = excluded_users
  302. if 'included-regions' in data or 'excluded-regions' in data:
  303. ext = XML.SubElement(exts_node,
  304. 'hudson.plugins.git.extensions.impl.'
  305. 'PathRestriction')
  306. if 'included-regions' in data:
  307. include_string = '\n'.join(data['included-regions'])
  308. XML.SubElement(ext, 'includedRegions').text = include_string
  309. if 'excluded-regions' in data:
  310. exclude_string = '\n'.join(data['excluded-regions'])
  311. XML.SubElement(ext, 'excludedRegions').text = exclude_string
  312. if 'ignore-commits-with-messages' in data:
  313. for msg in data['ignore-commits-with-messages']:
  314. ext_name = impl_prefix + 'MessageExclusion'
  315. ext = XML.SubElement(exts_node, ext_name)
  316. XML.SubElement(ext, 'excludedMessage').text = msg
  317. if 'local-branch' in data:
  318. ext = XML.SubElement(exts_node, impl_prefix + 'LocalBranch')
  319. XML.SubElement(ext, 'localBranch').text = str(data['local-branch'])
  320. if 'merge' in data:
  321. merge = data['merge']
  322. merge_strategies = ['default', 'resolve', 'recursive', 'octopus',
  323. 'ours', 'subtree']
  324. fast_forward_modes = ['FF', 'FF_ONLY', 'NO_FF']
  325. name = merge.get('remote', 'origin')
  326. branch = merge['branch']
  327. ext = XML.SubElement(exts_node, impl_prefix + 'PreBuildMerge')
  328. merge_opts = XML.SubElement(ext, 'options')
  329. XML.SubElement(merge_opts, 'mergeRemote').text = name
  330. XML.SubElement(merge_opts, 'mergeTarget').text = branch
  331. strategy = merge.get('strategy', 'default')
  332. if strategy not in merge_strategies:
  333. raise InvalidAttributeError('strategy', strategy, merge_strategies)
  334. XML.SubElement(merge_opts, 'mergeStrategy').text = strategy
  335. fast_forward_mode = merge.get('fast-forward-mode', 'FF')
  336. if fast_forward_mode not in fast_forward_modes:
  337. raise InvalidAttributeError('fast-forward-mode', fast_forward_mode,
  338. fast_forward_modes)
  339. XML.SubElement(merge_opts, 'fastForwardMode').text = fast_forward_mode
  340. if 'scm-name' in data:
  341. ext = XML.SubElement(exts_node, impl_prefix + 'ScmName')
  342. XML.SubElement(ext, 'name').text = str(data['scm-name'])
  343. clone_options = (
  344. "shallow-clone",
  345. "timeout",
  346. "do-not-fetch-tags"
  347. )
  348. if any(key in data for key in clone_options):
  349. clo = XML.SubElement(exts_node, impl_prefix + 'CloneOption')
  350. clone_mapping = [
  351. ('shallow-clone', 'shallow', False),
  352. ('depth', 'depth', 1),
  353. ]
  354. helpers.convert_mapping_to_xml(
  355. clo, data, clone_mapping, fail_required=True)
  356. if 'do-not-fetch-tags' in data:
  357. XML.SubElement(clo, 'noTags').text = str(
  358. data.get('do-not-fetch-tags', False)).lower()
  359. if 'timeout' in data:
  360. XML.SubElement(clo, 'timeout').text = str(data['timeout'])
  361. if 'sparse-checkout' in data:
  362. ext_name = impl_prefix + 'SparseCheckoutPaths'
  363. ext = XML.SubElement(exts_node, ext_name)
  364. sparse_co = XML.SubElement(ext, 'sparseCheckoutPaths')
  365. sparse_paths = data['sparse-checkout'].get('paths')
  366. if sparse_paths is not None:
  367. path_tagname = impl_prefix + 'SparseCheckoutPath'
  368. for path in sparse_paths:
  369. path_tag = XML.SubElement(sparse_co, path_tagname)
  370. XML.SubElement(path_tag, 'path').text = path
  371. if 'submodule' in data:
  372. ext_name = impl_prefix + 'SubmoduleOption'
  373. ext = XML.SubElement(exts_node, ext_name)
  374. XML.SubElement(ext, 'disableSubmodules').text = str(
  375. data['submodule'].get('disable', False)).lower()
  376. XML.SubElement(ext, 'recursiveSubmodules').text = str(
  377. data['submodule'].get('recursive', False)).lower()
  378. XML.SubElement(ext, 'trackingSubmodules').text = str(
  379. data['submodule'].get('tracking', False)).lower()
  380. XML.SubElement(ext, 'parentCredentials').text = str(
  381. data['submodule'].get('parent-credentials', False)).lower()
  382. XML.SubElement(ext, 'reference').text = str(
  383. data['submodule'].get('reference-repo', ''))
  384. XML.SubElement(ext, 'timeout').text = str(
  385. data['submodule'].get('timeout', 10))
  386. if 'timeout' in data:
  387. co = XML.SubElement(exts_node, impl_prefix + 'CheckoutOption')
  388. XML.SubElement(co, 'timeout').text = str(data['timeout'])
  389. polling_using_workspace = str(data.get('force-polling-using-workspace',
  390. False)).lower()
  391. if polling_using_workspace == 'true':
  392. ext_name = impl_prefix + 'DisableRemotePoll'
  393. ext = XML.SubElement(exts_node, ext_name)
  394. if 'per-build-tag' in data or 'skip-tag' in data:
  395. # We want to support both skip-tag (the old option) and per-build-tag
  396. # (the new option), with the new one overriding the old one.
  397. # Unfortunately they have inverse meanings, so we have to be careful.
  398. # The default value of per-build-tag is False if skip-tag is not set,
  399. # so we set the default value of skip-tag to True.
  400. per_build_tag_default = False
  401. if str(data.get('skip-tag', True)).lower() == 'false':
  402. per_build_tag_default = True
  403. if str(data.get('per-build-tag',
  404. per_build_tag_default)).lower() == 'true':
  405. XML.SubElement(exts_node, impl_prefix + 'PerBuildTag')
  406. prune = str(data.get('prune', False)).lower()
  407. if prune == 'true':
  408. XML.SubElement(exts_node, impl_prefix + 'PruneStaleBranch')
  409. ignore_notify_commits = str(data.get('ignore-notify', False)).lower()
  410. if ignore_notify_commits == 'true':
  411. XML.SubElement(exts_node, impl_prefix + 'IgnoreNotifyCommit')
  412. # By default we wipe the workspace
  413. wipe_workspace = str(data.get('wipe-workspace', True)).lower()
  414. if wipe_workspace == 'true':
  415. ext_name = impl_prefix + 'WipeWorkspace'
  416. ext = XML.SubElement(exts_node, ext_name)
  417. use_author = str(data.get('use-author', False)).lower()
  418. if use_author == 'true':
  419. XML.SubElement(exts_node, impl_prefix + 'AuthorInChangelog')
  420. browser = data.get('browser', 'auto')
  421. browserdict = {
  422. 'auto': 'auto',
  423. 'assemblaweb': 'AssemblaWeb',
  424. 'bitbucketweb': 'BitbucketWeb',
  425. 'cgit': 'CGit',
  426. 'fisheye': 'FisheyeGitRepositoryBrowser',
  427. 'gitblit': 'GitBlitRepositoryBrowser',
  428. 'githubweb': 'GithubWeb',
  429. 'gitiles': 'Gitiles',
  430. 'gitlab': 'GitLab',
  431. 'gitlist': 'GitList',
  432. 'gitoriousweb': 'GitoriousWeb',
  433. 'gitweb': 'GitWeb',
  434. 'kiln': 'KilnGit',
  435. 'microsoft-tfs-2013': 'TFS2013GitRepositoryBrowser',
  436. 'phabricator': 'Phabricator',
  437. 'redmineweb': 'RedmineWeb',
  438. 'rhodecode': 'RhodeCode',
  439. 'stash': 'Stash',
  440. 'viewgit': 'ViewGitWeb',
  441. }
  442. if browser not in browserdict:
  443. valid = sorted(browserdict.keys())
  444. raise JenkinsJobsException("Browser entered is not valid must be one "
  445. "of: %s or %s." % (", ".join(valid[:-1]),
  446. valid[-1]))
  447. if browser != 'auto':
  448. bc = XML.SubElement(scm, 'browser', {'class':
  449. 'hudson.plugins.git.browser.' +
  450. browserdict[browser]})
  451. XML.SubElement(bc, 'url').text = data['browser-url']
  452. if browser in ['gitblit', 'viewgit']:
  453. XML.SubElement(bc, 'projectName').text = str(
  454. data.get('project-name', ''))
  455. if browser == 'gitlab':
  456. XML.SubElement(bc, 'version').text = str(
  457. data.get('browser-version', '0.0'))
  458. if browser == 'phabricator':
  459. XML.SubElement(bc, 'repo').text = str(
  460. data.get('repo-name', ''))
  461. def cvs(registry, xml_parent, data):
  462. """yaml: cvs
  463. Specifies the CVS SCM repository for this job.
  464. Requires the Jenkins :jenkins-wiki:`CVS Plugin <CVS+Plugin>`.
  465. :arg list repos: List of CVS repositories. (required)
  466. :Repos:
  467. * **root** (`str`) -- The CVS connection string Jenkins uses to
  468. connect to the server. The format is :protocol:user@host:path
  469. (required)
  470. * **locations** (`list`) -- List of locations. (required)
  471. :Locations:
  472. * **type** (`str`) -- Type of location.
  473. :supported values:
  474. * **HEAD** - (default)
  475. * **BRANCH**
  476. * **TAG**
  477. * **name** (`str`) -- Name of location. Only valid in case
  478. of 'BRANCH' or 'TAG' location type. (default '')
  479. * **use-head** (`bool`) -- Use Head if not found. Only
  480. valid in case of 'BRANCH' or 'TAG' location type.
  481. (default false)
  482. * **modules** (`list`) -- List of modules. (required)
  483. :Modules:
  484. * **remote** -- The name of the module in the
  485. repository at CVSROOT. (required)
  486. * **local-name** -- The name to be applied to
  487. this module in the local workspace. If blank,
  488. the remote module name will be used.
  489. (default '')
  490. * **excluded-regions** (`list str`) -- Patterns for excluding
  491. regions. (optional)
  492. * **compression-level** (`int`) -- Compression level. Must be a
  493. number between -1 and 9 inclusive. Choose -1 for System Default.
  494. (default -1)
  495. :arg bool use-update: If true, Jenkins will use 'cvs update' whenever
  496. possible for builds. This makes a build faster. But this also causes the
  497. artifacts from the previous build to remain in the file system when a
  498. new build starts, making it not a true clean build. (default true)
  499. :arg bool prune-empty: Remove empty directories after checkout using the
  500. CVS '-P' option. (default true)
  501. :arg bool skip-changelog: Prevent the changelog being generated after
  502. checkout has completed. (default false)
  503. :arg bool show-all-output: Instructs CVS to show all logging output. CVS
  504. normally runs in quiet mode but this option disables that.
  505. (default false)
  506. :arg bool clean-checkout: Perform clean checkout on failed update.
  507. (default false)
  508. :arg bool clean-copy: Force clean copy for locally modified files.
  509. (default false)
  510. Example
  511. .. literalinclude:: /../../tests/scm/fixtures/cvs001.yaml
  512. :language: yaml
  513. .. literalinclude:: /../../tests/scm/fixtures/cvs002.yaml
  514. :language: yaml
  515. """
  516. prefix = 'hudson.scm.'
  517. valid_loc_types = {
  518. 'HEAD': 'Head',
  519. 'TAG': 'Tag',
  520. 'BRANCH': 'Branch'
  521. }
  522. cvs = XML.SubElement(xml_parent, 'scm', {'class': prefix + 'CVSSCM'})
  523. repos = data.get('repos')
  524. repos_tag = XML.SubElement(cvs, 'repositories')
  525. for repo in repos:
  526. repo_tag = XML.SubElement(repos_tag, prefix + 'CvsRepository')
  527. compression_level = repo.get('compression-level', '-1')
  528. repo_mapping = [
  529. ('root', 'cvsRoot', None),
  530. ('', 'compressionLevel', int(compression_level), range(-1, 10)),
  531. ]
  532. helpers.convert_mapping_to_xml(repo_tag,
  533. repo, repo_mapping, fail_required=True)
  534. items_tag = XML.SubElement(repo_tag, 'repositoryItems')
  535. locations = repo.get('locations')
  536. for location in locations:
  537. item_tag = XML.SubElement(items_tag, prefix + 'CvsRepositoryItem')
  538. loc_type = location.get('type', 'HEAD')
  539. if loc_type not in valid_loc_types:
  540. raise InvalidAttributeError('type', loc_type, valid_loc_types)
  541. loc_class = ('{0}CvsRepositoryLocation${1}Repository'
  542. 'Location').format(prefix, valid_loc_types[loc_type])
  543. loc_tag = XML.SubElement(item_tag, 'location',
  544. {'class': loc_class})
  545. mapping = [
  546. ('type', 'locationType', 'HEAD'),
  547. ]
  548. helpers.convert_mapping_to_xml(
  549. loc_tag, location, mapping, fail_required=True)
  550. if loc_type != 'HEAD':
  551. mapping = [
  552. ('name', 'locationName', ''),
  553. ('use-head', 'useHeadIfNotFound', False),
  554. ]
  555. helpers.convert_mapping_to_xml(
  556. loc_tag, location, mapping, fail_required=True)
  557. modules = location.get('modules')
  558. modules_tag = XML.SubElement(item_tag, 'modules')
  559. for module in modules:
  560. module_tag = XML.SubElement(modules_tag, prefix + 'CvsModule')
  561. mapping = [
  562. ('remote', 'remoteName', None),
  563. ('local-name', 'localName', ''),
  564. ]
  565. helpers.convert_mapping_to_xml(
  566. module_tag, module, mapping, fail_required=True)
  567. excluded = repo.get('excluded-regions', [])
  568. excluded_tag = XML.SubElement(repo_tag, 'excludedRegions')
  569. for pattern in excluded:
  570. pattern_tag = XML.SubElement(excluded_tag,
  571. prefix + 'ExcludedRegion')
  572. XML.SubElement(pattern_tag, 'pattern').text = pattern
  573. mappings = [
  574. ('use-update', 'canUseUpdate', True),
  575. ('prune-empty', 'pruneEmptyDirectories', True),
  576. ('skip-changelog', 'skipChangeLog', False),
  577. ('show-all-output', 'disableCvsQuiet', False),
  578. ('clean-checkout', 'cleanOnFailedUpdate', False),
  579. ('clean-copy', 'forceCleanCopy', False),
  580. ]
  581. helpers.convert_mapping_to_xml(cvs, data, mappings, fail_required=True)
  582. def repo(registry, xml_parent, data):
  583. """yaml: repo
  584. Specifies the repo SCM repository for this job.
  585. Requires the Jenkins :jenkins-wiki:`Repo Plugin <Repo+Plugin>`.
  586. :arg str manifest-url: URL of the repo manifest (required)
  587. :arg str manifest-branch: The branch of the manifest to use (optional)
  588. :arg str manifest-file: Initial manifest file to use when initialising
  589. (optional)
  590. :arg str manifest-group: Only retrieve those projects in the manifest
  591. tagged with the provided group name (optional)
  592. :arg list(str) ignore-projects: a list of projects in which changes would
  593. not be considered to trigger a build when pooling (optional)
  594. :arg str destination-dir: Location relative to the workspace root to clone
  595. under (optional)
  596. :arg str repo-url: custom url to retrieve the repo application (optional)
  597. :arg str mirror-dir: Path to mirror directory to reference when
  598. initialising (optional)
  599. :arg int jobs: Number of projects to fetch simultaneously (default 0)
  600. :arg int depth: Specify the depth in history to sync from the source. The
  601. default is to sync all of the history. Use 1 to just sync the most
  602. recent commit (default 0)
  603. :arg bool current-branch: Fetch only the current branch from the server
  604. (default true)
  605. :arg bool reset-first: Remove any commits that are not on the repositories
  606. by running the following command before anything else (default false):
  607. ``repo forall -c "git reset --hard"``
  608. :arg bool quiet: Make repo more quiet
  609. (default true)
  610. :arg bool force-sync: Continue sync even if a project fails to sync
  611. (default false)
  612. :arg bool no-tags: Don't fetch tags (default false)
  613. :arg bool trace: Trace git command execution into the build logs. (default
  614. false)
  615. :arg bool show-all-changes: When this is checked --first-parent is no
  616. longer passed to git log when determining changesets (default false)
  617. :arg str local-manifest: Contents of .repo/local_manifest.xml, written
  618. prior to calling sync (optional)
  619. Example:
  620. .. literalinclude:: /../../tests/scm/fixtures/repo001.yaml
  621. """
  622. scm = XML.SubElement(xml_parent,
  623. 'scm', {'class': 'hudson.plugins.repo.RepoScm'})
  624. mapping = [
  625. # option, xml name, default value
  626. ('manifest-url', 'manifestRepositoryUrl', None),
  627. ('jobs', 'jobs', 0),
  628. ('depth', 'depth', 0),
  629. ('current-branch', 'currentBranch', True),
  630. ('reset-first', 'resetFirst', False),
  631. ('quiet', 'quiet', True),
  632. ('force-sync', 'forceSync', False),
  633. ('no-tags', 'noTags', False),
  634. ('trace', 'trace', False),
  635. ('show-all-changes', 'showAllChanges', False),
  636. ]
  637. helpers.convert_mapping_to_xml(scm, data, mapping, fail_required=True)
  638. optional_mapping = [
  639. # option, xml name, default value
  640. ('manifest-branch', 'manifestBranch', None),
  641. ('manifest-file', 'manifestFile', None),
  642. ('manifest-group', 'manifestGroup', None),
  643. ('destination-dir', 'destinationDir', None),
  644. ('repo-url', 'repoUrl', None),
  645. ('mirror-dir', 'mirrorDir', None),
  646. ('local-manifest', 'localManifest', None),
  647. ]
  648. helpers.convert_mapping_to_xml(
  649. scm, data, optional_mapping, fail_required=False)
  650. # ignore-projects does not follow the same pattern of the other parameters,
  651. # so process it here:
  652. ip = XML.SubElement(scm, 'ignoreProjects', {'class': 'linked-hash-set'})
  653. ignored_projects = data.get('ignore-projects', [''])
  654. for ignored_project in ignored_projects:
  655. XML.SubElement(ip, 'string').text = str(ignored_project)
  656. def store(registry, xml_parent, data):
  657. """yaml: store
  658. Specifies the Visualworks Smalltalk Store repository for this job.
  659. Requires the Jenkins :jenkins-wiki:`Visualworks Smalltalk Store Plugin
  660. <Visualworks+Smalltalk+Store+Plugin>`.
  661. :arg str script: name of the Store script to run
  662. :arg str repository: name of the Store repository
  663. :arg str version-regex: regular expression that specifies which pundle
  664. versions should be considered (optional)
  665. :arg str minimum-blessing: minimum blessing level to consider (optional)
  666. :arg str parcel-builder-file: name of the file to generate as input to
  667. a later parcel building step (optional - if not specified, then no
  668. parcel builder file will be generated)
  669. :arg list pundles:
  670. :(package or bundle): (`dict`): A package or bundle to check
  671. Example:
  672. .. literalinclude:: /../../tests/scm/fixtures/store001.yaml
  673. """
  674. namespace = 'org.jenkinsci.plugins.visualworks_store'
  675. scm = XML.SubElement(xml_parent, 'scm',
  676. {'class': '{0}.StoreSCM'.format(namespace)})
  677. mapping = [
  678. ('script', 'scriptName', None),
  679. ('repository', 'repositoryName', None),
  680. ]
  681. helpers.convert_mapping_to_xml(scm, data, mapping, fail_required=True)
  682. pundle_specs = data.get('pundles', [])
  683. if not pundle_specs:
  684. raise JenkinsJobsException("At least one pundle must be specified")
  685. valid_pundle_types = ['PACKAGE', 'BUNDLE']
  686. pundles = XML.SubElement(scm, 'pundles')
  687. for pundle_spec in pundle_specs:
  688. pundle = XML.SubElement(pundles, '{0}.PundleSpec'.format(namespace))
  689. pundle_type = next(iter(pundle_spec))
  690. pundle_name = pundle_spec[pundle_type]
  691. mapping = [
  692. ('', 'name', pundle_name),
  693. ('', 'pundleType', pundle_type.upper(), valid_pundle_types),
  694. ]
  695. helpers.convert_mapping_to_xml(
  696. pundle, data, mapping, fail_required=True)
  697. generate_parcel = 'parcel-builder-file' in data
  698. mapping_optional = [
  699. ('version-regex', 'versionRegex', None),
  700. ('minimum-blessing', 'minimumBlessingLevel', None),
  701. ('', 'generateParcelBuilderInputFile', generate_parcel),
  702. ('parcel-builder-file', 'parcelBuilderInputFilename', None),
  703. ]
  704. helpers.convert_mapping_to_xml(scm,
  705. data, mapping_optional, fail_required=False)
  706. def svn(registry, xml_parent, data):
  707. """yaml: svn
  708. Specifies the svn SCM repository for this job.
  709. :arg str url: URL of the svn repository
  710. :arg str basedir: location relative to the workspace root to checkout to
  711. (default '.')
  712. :arg str credentials-id: optional argument to specify the ID of credentials
  713. to use
  714. :arg str repo-depth: Repository depth. Can be one of 'infinity', 'empty',
  715. 'files', 'immediates' or 'unknown'. (default 'infinity')
  716. :arg bool ignore-externals: Ignore Externals. (default false)
  717. :arg str workspaceupdater: optional argument to specify
  718. :arg str workspaceupdater: optional argument to specify how to update the
  719. workspace (default wipeworkspace)
  720. :supported values:
  721. * **wipeworkspace** - deletes the workspace before checking out
  722. * **revertupdate** - do an svn revert then an svn update
  723. * **emulateclean** - delete unversioned/ignored files then update
  724. * **update** - do an svn update as much as possible
  725. :arg list(str) excluded-users: list of users to ignore revisions from
  726. when polling for changes (if polling is enabled; parameter is optional)
  727. :arg list(str) included-regions: list of file/folders to include
  728. (optional)
  729. :arg list(str) excluded-regions: list of file/folders to exclude (optional)
  730. :arg list(str) excluded-commit-messages: list of commit messages to exclude
  731. (optional)
  732. :arg str exclusion-revprop-name: revision svn-property to ignore (optional)
  733. :arg bool ignore-property-changes-on-directories: ignore svn-property only
  734. changes of directories (default false)
  735. :arg bool filter-changelog: If set Jenkins will apply the same inclusion
  736. and exclusion patterns for displaying changelog entries as it does for
  737. polling for changes (default false)
  738. :arg list repos: list of repositories to checkout (optional)
  739. :arg list additional-credentials: list of additional credentials (optional)
  740. :Additional-Credentials:
  741. * **realm** (`str`) -- realm to use
  742. * **credentials-id** (`str`) -- optional ID of credentials to use
  743. :arg str viewvc-url: URL of the svn web interface (optional)
  744. :Repo:
  745. * **url** (`str`) -- URL for the repository
  746. * **basedir** (`str`) -- Location relative to the workspace root
  747. to checkout to (default '.')
  748. * **credentials-id** - optional ID of credentials to use
  749. * **repo-depth** - Repository depth. Can be one of 'infinity',
  750. 'empty', 'files', 'immediates' or 'unknown'. (default 'infinity')
  751. * **ignore-externals** - Ignore Externals. (default false)
  752. Multiple repos example:
  753. .. literalinclude:: /../../tests/scm/fixtures/svn-multiple-repos-001.yaml
  754. Advanced commit filtering example:
  755. .. literalinclude:: /../../tests/scm/fixtures/svn-regions-001.yaml
  756. """
  757. scm = XML.SubElement(xml_parent, 'scm', {'class':
  758. 'hudson.scm.SubversionSCM'})
  759. if 'viewvc-url' in data:
  760. browser = XML.SubElement(
  761. scm, 'browser', {'class': 'hudson.scm.browsers.ViewSVN'})
  762. mapping = [
  763. ('viewvc-url', 'url', None),
  764. ]
  765. helpers.convert_mapping_to_xml(
  766. browser, data, mapping, fail_required=True)
  767. locations = XML.SubElement(scm, 'locations')
  768. def populate_repo_xml(parent, data):
  769. module = XML.SubElement(parent,
  770. 'hudson.scm.SubversionSCM_-ModuleLocation')
  771. mapping = [
  772. ('url', 'remote', None),
  773. ('basedir', 'local', '.'),
  774. ]
  775. helpers.convert_mapping_to_xml(
  776. module, data, mapping, fail_required=True)
  777. repo_depths = ['infinity', 'empty', 'files', 'immediates', 'unknown']
  778. mapping_optional = [
  779. ('credentials-id', 'credentialsId', None),
  780. ('repo-depth', 'depthOption', 'infinity', repo_depths),
  781. ('ignore-externals', 'ignoreExternalsOption', False),
  782. ]
  783. helpers.convert_mapping_to_xml(module, data,
  784. mapping_optional, fail_required=False)
  785. if 'repos' in data:
  786. repos = data['repos']
  787. for repo in repos:
  788. populate_repo_xml(locations, repo)
  789. elif 'url' in data:
  790. populate_repo_xml(locations, data)
  791. else:
  792. raise JenkinsJobsException("A top level url or repos list must exist")
  793. def populate_additional_credential_xml(parent, data):
  794. module = XML.SubElement(parent,
  795. 'hudson.scm.SubversionSCM_-AdditionalCredentials')
  796. XML.SubElement(module, 'realm').text = data['realm']
  797. if 'credentials-id' in data:
  798. XML.SubElement(module, 'credentialsId').text = data[
  799. 'credentials-id']
  800. if 'additional-credentials' in data:
  801. additional_credentials = XML.SubElement(scm, 'additionalCredentials')
  802. additional_credentials_data = data['additional-credentials']
  803. for additional_credential in additional_credentials_data:
  804. populate_additional_credential_xml(additional_credentials,
  805. additional_credential)
  806. updater = data.get('workspaceupdater', 'wipeworkspace')
  807. if updater == 'wipeworkspace':
  808. updaterclass = 'CheckoutUpdater'
  809. elif updater == 'revertupdate':
  810. updaterclass = 'UpdateWithRevertUpdater'
  811. elif updater == 'emulateclean':
  812. updaterclass = 'UpdateWithCleanUpdater'
  813. elif updater == 'update':
  814. updaterclass = 'UpdateUpdater'
  815. XML.SubElement(scm, 'workspaceUpdater', {'class':
  816. 'hudson.scm.subversion.' + updaterclass})
  817. mapping = [
  818. # option, xml name, default value
  819. ("excluded-regions", 'excludedRegions', []),
  820. ("included-regions", 'includedRegions', []),
  821. ("excluded-users", 'excludedUsers', []),
  822. ("exclusion-revprop-name", 'excludedRevprop', ''),
  823. ("excluded-commit-messages", 'excludedCommitMessages', []),
  824. ("ignore-property-changes-on-directories", 'ignoreDirPropChanges',
  825. False),
  826. ("filter-changelog", 'filterChangelog', False),
  827. ]
  828. for optname, xmlname, defvalue in mapping:
  829. if isinstance(defvalue, list):
  830. val = '\n'.join(data.get(optname, defvalue))
  831. else:
  832. val = data.get(optname, defvalue)
  833. # Skip adding xml entry if default is empty and no value given
  834. if not val and (defvalue in ['', []]):
  835. continue
  836. xe = XML.SubElement(scm, xmlname)
  837. if isinstance(defvalue, bool):
  838. xe.text = str(val).lower()
  839. else:
  840. xe.text = str(val)
  841. def tfs(registry, xml_parent, data):
  842. """yaml: tfs
  843. Specifies the Team Foundation Server repository for this job.
  844. Requires the Jenkins :jenkins-wiki:`Team Foundation Server Plugin
  845. <Team+Foundation+Server+Plugin>`.
  846. **NOTE**: TFS Password must be entered manually on the project if a
  847. user name is specified. The password will be overwritten with an empty
  848. value every time the job is rebuilt with Jenkins Job Builder.
  849. :arg str server-url: The name or URL of the team foundation server.
  850. If the server has been registered on the machine then it is only
  851. necessary to enter the name.
  852. :arg str project-path: The name of the project as it is registered on the
  853. server.
  854. :arg str login: The user name that is registered on the server. The user
  855. name must contain the name and the domain name. Entered as
  856. domain\\\\user or user\@domain (optional).
  857. **NOTE**: You must enter in at least two slashes for the
  858. domain\\\\user format in JJB YAML. It will be rendered normally.
  859. :arg str use-update: If true, Hudson will not delete the workspace at end
  860. of each build. This causes the artifacts from the previous build to
  861. remain when a new build starts. (default true)
  862. :arg str local-path: The folder where all files will be retrieved into.
  863. The folder name is a relative path, under the workspace of the current
  864. job. (default .)
  865. :arg str workspace: The name of the workspace under which the source
  866. should be retrieved. This workspace is created at the start of a
  867. download, and deleted at the end. You can normally omit the property
  868. unless you want to name a workspace to avoid conflicts on the server
  869. (i.e. when you have multiple projects on one server talking to a
  870. Team Foundation Server). (default Hudson-${JOB_NAME}-${NODE_NAME})
  871. The TFS plugin supports the following macros that are replaced in the
  872. workspace name:
  873. * ${JOB_NAME} - The name of the job.
  874. * ${USER_NAME} - The user name that the Hudson server or slave is
  875. running as.
  876. * ${NODE_NAME} - The name of the node/slave that the plugin currently
  877. is executed on. Note that this is not the hostname, this value is
  878. the Hudson configured name of the slave/node.
  879. * ${ENV} - The environment variable that is set on the master or slave.
  880. :arg dict web-access: Adds links in "changes" views within Jenkins to an
  881. external system for browsing the details of those changes. The "Auto"
  882. selection attempts to infer the repository browser from other jobs,
  883. if supported by the SCM and a job with matching SCM details can be
  884. found. (optional, default Auto).
  885. :web-access value:
  886. * **web-url** -- Enter the URL to the TSWA server. The plugin will
  887. strip the last path (if any) of the URL when building URLs for
  888. change set pages and other pages. (optional, default
  889. uses server-url)
  890. Examples:
  891. .. literalinclude:: /../../tests/scm/fixtures/tfs-001.yaml
  892. .. literalinclude:: /../../tests/scm/fixtures/tfs-002.yaml
  893. """
  894. tfs = XML.SubElement(xml_parent, 'scm',
  895. {'class': 'hudson.plugins.tfs.'
  896. 'TeamFoundationServerScm'})
  897. mapping = [
  898. ('server-url', 'serverUrl', ''),
  899. ('project-path', 'projectPath', ''),
  900. ('local-path', 'localPath', '.'),
  901. ('workspace', 'workspaceName', 'Hudson-${JOB_NAME}-${NODE_NAME}'),
  902. # TODO: In the future, it would be nice to have a place that can pull
  903. # passwords into JJB without having to commit them in plaintext. This
  904. # could also integrate nicely with global configuration options.
  905. ('', 'userPassword', ''),
  906. ('login', 'userName', ''),
  907. ('use-update', 'useUpdate', True),
  908. ]
  909. helpers.convert_mapping_to_xml(tfs, data, mapping, fail_required=True)
  910. store = data.get('web-access', None)
  911. if isinstance(store, list):
  912. web = XML.SubElement(tfs, 'repositoryBrowser',
  913. {'class': 'hudson.plugins.tfs.browsers.'
  914. 'TeamSystemWebAccessBrowser'})
  915. XML.SubElement(web, 'url').text = str(store[0].get('web-url', None))
  916. elif 'web-access' in data and store is None:
  917. XML.SubElement(tfs, 'repositoryBrowser', {'class': 'hudson.'
  918. 'plugins.tfs.browsers.'
  919. 'TeamSystemWebAccess'
  920. 'Browser'})
  921. def workspace(registry, xml_parent, data):
  922. """yaml: workspace
  923. Specifies the cloned workspace for this job to use as a SCM source.
  924. Requires the Jenkins :jenkins-wiki:`Clone Workspace SCM Plugin
  925. <Clone+Workspace+SCM+Plugin>`.
  926. The job the workspace is cloned from must be configured with an
  927. clone-workspace publisher
  928. :arg str parent-job: The name of the parent job to clone the
  929. workspace from.
  930. :arg str criteria: Set the criteria to determine what build of the parent
  931. project to use. Can be one of 'Any', 'Not Failed' or 'Successful'.
  932. (default Any)
  933. Example:
  934. .. literalinclude:: /../../tests/scm/fixtures/workspace001.yaml
  935. """
  936. workspace = XML.SubElement(xml_parent, 'scm', {'class': 'hudson.plugins.'
  937. 'cloneworkspace.CloneWorkspaceSCM'})
  938. criteria_list = ['Any', 'Not Failed', 'Successful']
  939. criteria = data.get('criteria', 'Any').title()
  940. mapping = [
  941. ('parent-job', 'parentJobName', ''),
  942. ('', 'criteria', criteria, criteria_list),
  943. ]
  944. helpers.convert_mapping_to_xml(
  945. workspace, data, mapping, fail_required=True)
  946. def hg(self, xml_parent, data):
  947. """yaml: hg
  948. Specifies the mercurial SCM repository for this job.
  949. Requires the Jenkins :jenkins-wiki:`Mercurial Plugin <Mercurial+Plugin>`.
  950. :arg str url: URL of the hg repository (required)
  951. :arg str credentials-id: ID of credentials to use to connect (optional)
  952. :arg str revision-type: revision type to use (default 'branch')
  953. :arg str revision: the branch or tag name you would like to track
  954. (default 'default')
  955. :arg list(str) modules: reduce unnecessary builds by specifying a list of
  956. "modules" within the repository. A module is a directory name within
  957. the repository that this project lives in. (default '')
  958. :arg bool clean: wipe any local modifications or untracked files in the
  959. repository checkout (default false)
  960. :arg str subdir: check out the Mercurial repository into this
  961. subdirectory of the job's workspace (optional)
  962. :arg bool disable-changelog: do not calculate the Mercurial changelog
  963. for each build (default false)
  964. :arg str browser: what repository browser to use
  965. :browsers supported:
  966. * **auto** - (default)
  967. * **bitbucketweb** - https://bitbucket.org/
  968. * **fisheye** - https://www.atlassian.com/software/fisheye
  969. * **googlecode** - https://code.google.com/
  970. * **hgweb** - https://www.selenic.com/hg/help/hgweb
  971. * **kilnhg** - https://www.fogcreek.com/kiln/
  972. * **rhodecode** - https://rhodecode.com/ (versions >= 1.2)
  973. * **rhodecode-pre-1.2.0** - https://rhodecode.com/ (versions < 1.2)
  974. :arg str browser-url: url for the repository browser
  975. (required if browser is set)
  976. Example:
  977. .. literalinclude:: ../../tests/scm/fixtures/hg02.yaml
  978. """
  979. revision_type_dict = {
  980. 'branch': 'BRANCH',
  981. 'tag': 'TAG',
  982. }
  983. browser = data.get('browser', 'auto')
  984. browserdict = {
  985. 'auto': '',
  986. 'bitbucket': 'BitBucket', # deprecated
  987. 'bitbucketweb': 'BitBucket',
  988. 'fisheye': 'FishEye',
  989. 'googlecode': 'GoogleCode',
  990. 'hgweb': 'HgWeb',
  991. 'kilnhg': 'KilnHG',
  992. 'rhodecode': 'RhodeCode',
  993. 'rhodecode-pre-1.2.0': 'RhodeCodeLegacy'
  994. }
  995. scm = XML.SubElement(xml_parent, 'scm', {'class':
  996. 'hudson.plugins.mercurial.MercurialSCM'})
  997. mapping = [
  998. ('url', 'source', None),
  999. ]
  1000. helpers.convert_mapping_to_xml(scm, data, mapping, fail_required=True)
  1001. mapping_optional = [
  1002. ('credentials-id', 'credentialsId', None),
  1003. ('revision-type', 'revisionType', 'branch', revision_type_dict),
  1004. ('revision', 'revision', 'default'),
  1005. ('subdir', 'subdir', None),
  1006. ('clean', 'clean', False),
  1007. ]
  1008. helpers.convert_mapping_to_xml(
  1009. scm, data, mapping_optional, fail_required=False)
  1010. modules = data.get('modules', '')
  1011. if isinstance(modules, list):
  1012. modules = " ".join(modules)
  1013. XML.SubElement(scm, 'modules').text = modules
  1014. XML.SubElement(scm, 'disableChangeLog').text = str(data.get(
  1015. 'disable-changelog', False)).lower()
  1016. if browser != 'auto':
  1017. bc = XML.SubElement(scm, 'browser',
  1018. {'class': 'hudson.plugins.mercurial.browser.' +
  1019. browserdict[browser]})
  1020. mapping = [
  1021. ('browser-url', 'url', None, browserdict[browser]),
  1022. ]
  1023. helpers.convert_mapping_to_xml(bc, data, mapping, fail_required=True)
  1024. def openshift_img_streams(registry, xml_parent, data):
  1025. """yaml: openshift-img-streams
  1026. Rather than a Build step extension plugin, this is an extension of the
  1027. Jenkins SCM plugin, where this baked-in polling mechanism provided by
  1028. Jenkins is leveraged by exposing some of the common semantics between
  1029. OpenShift ImageStreams (which are abstractions of Docker repositories)
  1030. and SCMs - versions / commit IDs of related artifacts
  1031. (images vs. programmatics files)
  1032. Requires the Jenkins :jenkins-wiki:`OpenShift
  1033. Pipeline Plugin <OpenShift+Pipeline+Plugin>`._
  1034. :arg str image-stream-name: The name of the ImageStream is what shows up
  1035. in the NAME column if you dump all the ImageStream's with the
  1036. `oc get is` command invocation. (default nodejs-010-centos7)
  1037. :arg str tag: The specific image tag within the ImageStream to monitor.
  1038. (default latest)
  1039. :arg str api-url: This would be the value you specify if you leverage the
  1040. --server option on the OpenShift `oc` command.
  1041. (default \https://openshift.default.svc.cluster.local\)
  1042. :arg str namespace: The value here should be whatever was the output
  1043. form `oc project` when you created the BuildConfig you want to run
  1044. a Build on. (default test)
  1045. :arg str auth-token: The value here is what you supply with the --token
  1046. option when invoking the OpenShift `oc` command. (default '')
  1047. :arg bool verbose: This flag is the toggle for
  1048. turning on or off detailed logging in this plug-in. (default false)
  1049. Full Example:
  1050. .. literalinclude::
  1051. ../../tests/scm/fixtures/openshift-img-streams001.yaml
  1052. :language: yaml
  1053. Minimal Example:
  1054. .. literalinclude::
  1055. ../../tests/scm/fixtures/openshift-img-streams002.yaml
  1056. :language: yaml
  1057. """
  1058. scm = XML.SubElement(xml_parent,
  1059. 'scm', {'class':
  1060. 'com.openshift.jenkins.plugins.pipeline.'
  1061. 'OpenShiftImageStreams'})
  1062. mapping = [
  1063. # option, xml name, default value
  1064. ("image-stream-name", 'imageStreamName', 'nodejs-010-centos7'),
  1065. ("tag", 'tag', 'latest'),
  1066. ("api-url", 'apiURL', 'https://openshift.default.svc.cluster.local'),
  1067. ("namespace", 'namespace', 'test'),
  1068. ("auth-token", 'authToken', ''),
  1069. ("verbose", 'verbose', False),
  1070. ]
  1071. helpers.convert_mapping_to_xml(scm, data, mapping, fail_required=True)
  1072. def bzr(registry, xml_parent, data):
  1073. """yaml: bzr
  1074. Specifies the bzr SCM repository for this job.
  1075. Requires the Jenkins :jenkins-wiki:`Bazaar Plugin <Bazaar+Plugin>`.
  1076. :arg str url: URL of the bzr branch (required)
  1077. :arg bool clean-tree: Clean up the workspace (using bzr) before pulling
  1078. the branch (default false)
  1079. :arg bool lightweight-checkout: Use a lightweight checkout instead of a
  1080. full branch (default false)
  1081. :arg str browser: The repository browser to use.
  1082. :browsers supported:
  1083. * **auto** - (default)
  1084. * **loggerhead** - as used by Launchpad
  1085. * **opengrok** - https://opengrok.github.io/OpenGrok/
  1086. :arg str browser-url:
  1087. URL for the repository browser (required if browser is set).
  1088. :arg str opengrok-root-module:
  1089. Root module for OpenGrok (required if browser is opengrok).
  1090. Example:
  1091. .. literalinclude:: /../../tests/scm/fixtures/bzr001.yaml
  1092. :language: yaml
  1093. """
  1094. mapping = [
  1095. # option, xml name, default value (text), attributes (hard coded)
  1096. ('url', 'source', None),
  1097. ('clean-tree', 'cleantree', False),
  1098. ('lightweight-checkout', 'checkout', False),
  1099. ]
  1100. scm_element = XML.SubElement(
  1101. xml_parent, 'scm', {'class': 'hudson.plugins.bazaar.BazaarSCM'})
  1102. helpers.convert_mapping_to_xml(
  1103. scm_element, data, mapping, fail_required=True)
  1104. browser_name_to_class = {
  1105. 'loggerhead': 'Loggerhead',
  1106. 'opengrok': 'OpenGrok',
  1107. }
  1108. browser = data.get('browser', 'auto')
  1109. if browser == 'auto':
  1110. return
  1111. if browser not in browser_name_to_class:
  1112. raise InvalidAttributeError('browser', browser,
  1113. browser_name_to_class.keys())
  1114. browser_element = XML.SubElement(
  1115. scm_element,
  1116. 'browser',
  1117. {'class': 'hudson.plugins.bazaar.browsers.{0}'.format(
  1118. browser_name_to_class[browser])})
  1119. mapping = [
  1120. ('browser-url', 'url', None),
  1121. ]
  1122. helpers.convert_mapping_to_xml(
  1123. browser_element, data, mapping, fail_required=True)
  1124. if browser == 'opengrok':
  1125. mapping = [
  1126. ('opengrok-root-module', 'rootModule', None),
  1127. ]
  1128. helpers.convert_mapping_to_xml(browser_element,
  1129. data, mapping, fail_required=True)
  1130. def url(registry, xml_parent, data):
  1131. """yaml: url
  1132. Watch for changes in, and download an artifact from a particular url.
  1133. Requires the Jenkins :jenkins-wiki:`URL SCM <URL+SCM>`.
  1134. :arg list url-list: List of URLs to watch. (required)
  1135. :arg bool clear-workspace: If set to true, clear the workspace before
  1136. downloading the artifact(s) specified in url-list. (default false)
  1137. Examples:
  1138. .. literalinclude:: ../../tests/scm/fixtures/url001.yaml
  1139. :language: yaml
  1140. .. literalinclude:: ../../tests/scm/fixtures/url002.yaml
  1141. :language: yaml
  1142. """
  1143. scm = XML.SubElement(xml_parent, 'scm', {'class':
  1144. 'hudson.plugins.URLSCM.URLSCM'})
  1145. urls = XML.SubElement(scm, 'urls')
  1146. for data_url in data['url-list']:
  1147. url_tuple = XML.SubElement(
  1148. urls, 'hudson.plugins.URLSCM.URLSCM_-URLTuple')
  1149. mapping = [
  1150. ('', 'urlString', data_url),
  1151. ]
  1152. helpers.convert_mapping_to_xml(
  1153. url_tuple, data, mapping, fail_required=True)
  1154. mapping = [
  1155. ('clear-workspace', 'clearWorkspace', False),
  1156. ]
  1157. helpers.convert_mapping_to_xml(scm, data, mapping, fail_required=True)
  1158. def dimensions(registry, xml_parent, data):
  1159. """yaml: dimensions
  1160. Specifies the Dimensions SCM repository for this job.
  1161. Requires Jenkins :jenkins-wiki:`Dimensions Plugin <Dimensions+Plugin>`.
  1162. :arg str project: Project name of format PRODUCT_ID:PROJECT_NAME (required)
  1163. :arg str permissions: Default Permissions for updated files
  1164. (default: DEFAULT)
  1165. :Permissions:
  1166. * **DEFAULT**
  1167. * **READONLY**
  1168. * **WRITABLE**
  1169. :arg str eol: End of line (default: DEFAULT)
  1170. :End of line:
  1171. * **DEFAULT**
  1172. * **UNIX**
  1173. * **WINDOWS**
  1174. * **UNCHANGED**
  1175. :arg list folders: Folders to monitor (default /)
  1176. :arg list exclude: Paths to exclude from monitor
  1177. :arg str username: Repository username for this job
  1178. :arg str password: Repository password for this job
  1179. :arg str server: Dimensions server for this job
  1180. :arg str database: Dimensions database for this job.
  1181. Format must be database@dsn
  1182. :arg bool update: Use update (default false)
  1183. :arg bool clear-workspace: Clear workspace prior to build (default false)
  1184. :arg bool force-build: Force build even if the repository SCM checkout
  1185. operation fails (default false)
  1186. :arg bool overwrite-modified: Overwrite files in worspace from
  1187. repository files (default false)
  1188. :arg bool expand-vars: Expand substitution variables (default false)
  1189. :arg bool no-metadata: Checkout files with no metadata (default false)
  1190. :arg bool maintain-timestamp: Maintain file timestamp from Dimensions
  1191. (default false)
  1192. :arg bool slave-checkout: Force slave based checkout (default false)
  1193. :arg str timezone: Server timezone
  1194. :arg str web-url: Dimensions Web URL
  1195. Examples:
  1196. .. literalinclude:: /../../tests/scm/fixtures/dimensions-minimal.yaml
  1197. :language: yaml
  1198. .. literalinclude:: /../../tests/scm/fixtures/dimensions-full.yaml
  1199. :language: yaml
  1200. """
  1201. scm = XML.SubElement(
  1202. xml_parent,
  1203. 'scm', {'class': 'hudson.plugins.dimensionsscm.DimensionsSCM'})
  1204. # List to check against for valid permission
  1205. perm = ['DEFAULT', 'READONLY', 'WRITABLE']
  1206. # List to check against for valid end of line
  1207. eol = ['DEFAULT', 'UNIX', 'WINDOWS', 'UNCHANGED']
  1208. mapping = [
  1209. # option, xml name, default value (text), attributes (hard coded)
  1210. ('project', 'project', None),
  1211. ('permissions', 'permissions', 'DEFAULT', perm),
  1212. ('eol', 'eol', 'DEFAULT', eol),
  1213. ('update', 'canJobUpdate', False),
  1214. ('clear-workspace', 'canJobDelete', False),
  1215. ('force-build', 'canJobForce', False),
  1216. ('overwrite-modified', 'canJobRevert', False),
  1217. ('expand-vars', 'canJobExpand', False),
  1218. ('no-metadata', 'canJobNoMetadata', False),
  1219. ('maintain-timestamp', 'canJobNoTouch', False),
  1220. ('slave-checkout', 'forceAsSlave', False),
  1221. ]
  1222. helpers.convert_mapping_to_xml(scm, data, mapping, fail_required=True)
  1223. # Folders to monitor. Default '/'
  1224. folders = XML.SubElement(scm, 'folders')
  1225. if 'folders' in data:
  1226. for folder in data['folders']:
  1227. XML.SubElement(folders, 'string').text = folder
  1228. else:
  1229. XML.SubElement(folders, 'string').text = '/'
  1230. # Excluded paths
  1231. exclude = XML.SubElement(scm, 'pathsToExclude')
  1232. if 'exclude' in data:
  1233. for exc in data['exclude']:
  1234. XML.SubElement(exclude, 'string').text = exc
  1235. optional_mapping = [
  1236. # option, xml name, default value (text), attributes (hard coded)
  1237. ('username', 'jobUserName', None),
  1238. ('password', 'jobPasswd', None),
  1239. ('server', 'jobServer', None),
  1240. ('database', 'jobDatabase', None),
  1241. ('timezone', 'jobTimeZone', None),
  1242. ('web-url', 'jobWebUrl', None),
  1243. ]
  1244. helpers.convert_mapping_to_xml(
  1245. scm, data, optional_mapping, fail_required=False)
  1246. def accurev(registry, xml_parent, data):
  1247. """yaml: accurev
  1248. Specifies the AccuRev SCM repository for this job.
  1249. Requires the Jenkins :jenkins-wiki:`AccuRev Plugin <AccuRev+Plugin>`.
  1250. :arg str depot: Depot you want to use for the current job (optional)
  1251. :arg str stream: Stream where the build will be generated from (optional)
  1252. :arg str server-name: AccuRev server you are using
  1253. for your builds (required)
  1254. :arg bool ignore-parent-changes: Ignore possibility
  1255. of changes in the parent stream (default false)
  1256. :arg bool clean-reference-tree: Deletes any external files
  1257. in reference tree (default false)
  1258. :arg bool build-from-snapshot: Creates snapshot
  1259. of the target stream, then populates and
  1260. builds from that snapshot (default false)
  1261. :arg bool do-not-pop-content: If checkbox is on, elements
  1262. are not populating vice versa (default false)
  1263. :arg str workspace: Name of existing workspace (optional)
  1264. :arg str reference-tree: Name of the reference tree (optional)
  1265. :arg str directory-offset: Relative directory path from
  1266. the default Jenkins workspace location
  1267. where the files from the stream, workspace,
  1268. or reference tree should be retrieved from. (optional)
  1269. :arg str sub-path: Makes a "best effort" to ensure
  1270. that only the sub-path is populated (optional)
  1271. :arg str filter-poll-scm: Specify directories or
  1272. files you want Jenkins to check before starting a build (optional)
  1273. :arg str snapshot-name-format: Naming conventions
  1274. for the snapshot in this field (optional)
  1275. Example:
  1276. .. literalinclude:: /../../tests/scm/fixtures/accurev001.yaml
  1277. """
  1278. scm = XML.SubElement(xml_parent,
  1279. 'scm', {'class': 'hudson.plugins.accurev.AccurevSCM'})
  1280. mapping = [
  1281. ('depot', 'depot', None),
  1282. ('stream', 'stream', None),
  1283. ('server-name', 'serverName', None),
  1284. ('ignore-parent-changes', 'ignoreStreamParent', False),
  1285. ('clean-reference-tree', 'cleanreftree', False),
  1286. ('build-from-snapshot', 'useSnapshot', False),
  1287. ('do-not-pop-content', 'dontPopContent', False),
  1288. ]
  1289. helpers.convert_mapping_to_xml(scm, data, mapping, fail_required=True)
  1290. additional_mapping = [
  1291. ('workspace', 'workspace', None),
  1292. ('reference-tree', 'reftree', None),
  1293. ('directory-offset', 'directoryOffset', None),
  1294. ('sub-path', 'subPath', None),
  1295. ('filter-poll-scm', 'filterForPollSCM', None),
  1296. ('snapshot-name-format', 'snapshotNameFormat', None),
  1297. ]
  1298. helpers.convert_mapping_to_xml(
  1299. scm, data, additional_mapping, fail_required=False)
  1300. class SCM(jenkins_jobs.modules.base.Base):
  1301. sequence = 30
  1302. component_type = 'scm'
  1303. component_list_type = 'scm'
  1304. def gen_xml(self, xml_parent, data):
  1305. # multibranch-pipeline scm implementation is incompatible with SCM
  1306. if data.get('project-type') in ['multibranch', 'multibranch-defaults']:
  1307. logging.debug("SCM Module skipped for multibranch project-type.")
  1308. return
  1309. scms_parent = XML.Element('scms')
  1310. for scm in data.get('scm', []):
  1311. self.registry.dispatch('scm', scms_parent, scm)
  1312. scms_count = len(scms_parent)
  1313. if scms_count == 0:
  1314. XML.SubElement(xml_parent, 'scm', {'class': 'hudson.scm.NullSCM'})
  1315. elif scms_count == 1:
  1316. xml_parent.append(scms_parent[0])
  1317. else:
  1318. class_name = 'org.jenkinsci.plugins.multiplescms.MultiSCM'
  1319. xml_attribs = {'class': class_name}
  1320. xml_parent = XML.SubElement(xml_parent, 'scm', xml_attribs)
  1321. for scms_child in scms_parent:
  1322. try:
  1323. scms_child.tag = scms_child.attrib['class']
  1324. del(scms_child.attrib['class'])
  1325. except KeyError:
  1326. pass
  1327. xml_parent.append(scms_parent)
  1328. class PipelineSCM(jenkins_jobs.modules.base.Base):
  1329. sequence = 30
  1330. component_type = 'pipeline-scm'
  1331. component_list_type = 'pipeline-scm'
  1332. def gen_xml(self, xml_parent, data):
  1333. definition_parent = xml_parent.find('definition')
  1334. pipeline_dict = data.get(self.component_type, {})
  1335. scms = pipeline_dict.get('scm')
  1336. if scms:
  1337. scms_count = len(scms)
  1338. if scms_count == 0:
  1339. raise JenkinsJobsException("'scm' missing or empty")
  1340. elif scms_count == 1:
  1341. self.registry.dispatch('scm', definition_parent, scms[0])
  1342. mapping = [
  1343. ('script-path', 'scriptPath', 'Jenkinsfile'),
  1344. ('lightweight-checkout', 'lightweight', None,
  1345. [True, False]),
  1346. ]
  1347. helpers.convert_mapping_to_xml(
  1348. definition_parent, pipeline_dict, mapping,
  1349. fail_required=False)
  1350. else:
  1351. raise JenkinsJobsException('Only one SCM can be specified '
  1352. 'as pipeline-scm')