diff --git a/README.rst b/README.rst
index 935513a54..9ebee9375 100644
--- a/README.rst
+++ b/README.rst
@@ -1,11 +1,11 @@
README
======
-Jenkins Job Builder takes simple descriptions of Jenkins_ jobs in YAML_ format,
-and uses them to configure Jenkins. You can keep your job descriptions in human
-readable text format in a version control system to make changes and auditing
-easier. It also has a flexible template system, so creating many similarly
-configured jobs is easy.
+Jenkins Job Builder takes simple descriptions of Jenkins_ jobs in YAML_ or JSON_
+format and uses them to configure Jenkins. You can keep your job descriptions in
+human readable text format in a version control system to make changes and
+auditing easier. It also has a flexible template system, so creating many
+similarly configured jobs is easy.
To install::
@@ -77,6 +77,7 @@ Then install the required python packages using pip_::
.. _Jenkins: http://jenkins-ci.org/
.. _YAML: http://www.yaml.org/
+.. _JSON: http://json.org/
.. _pep8: https://pypi.python.org/pypi/pep8
.. _pyflakes: https://pypi.python.org/pypi/pyflakes
.. _tox: https://testrun.org/tox
diff --git a/doc/source/definition.rst b/doc/source/definition.rst
index c5aa45986..f9e34a114 100644
--- a/doc/source/definition.rst
+++ b/doc/source/definition.rst
@@ -2,10 +2,10 @@ Job Definitions
===============
The job definitions for Jenkins Job Builder are kept in any number of
-YAML files, in whatever way you would like to organize them. When you
+YAML or JSON files, in whatever way you would like to organize them. When you
invoke ``jenkins-jobs`` you may specify either the path of a single
YAML file, or a directory. If you choose a directory, all of
-the .yaml (or .yml) files in that directory will be read, and all the
+the .yaml/.yml or .json files in that directory will be read, and all the
jobs they define will be created or updated.
Definitions
diff --git a/tests/base.py b/tests/base.py
index 517cfc980..1bc8f5c8d 100644
--- a/tests/base.py
+++ b/tests/base.py
@@ -78,7 +78,7 @@ def get_scenarios(fixtures_path, in_ext='yaml', out_ext='xml',
if plugins_info_candidate not in files:
plugins_info_candidate = None
- conf_candidate = re.sub(r'\.yaml$', '.conf', input_filename)
+ conf_candidate = re.sub(r'\.yaml$|\.json$', '.conf', input_filename)
# If present, add the configuration file
if conf_candidate not in files:
conf_candidate = None
diff --git a/tests/jsonparser/__init__.py b/tests/jsonparser/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/jsonparser/fixtures/complete001.json b/tests/jsonparser/fixtures/complete001.json
new file mode 100644
index 000000000..517e1cc2d
--- /dev/null
+++ b/tests/jsonparser/fixtures/complete001.json
@@ -0,0 +1,168 @@
+[
+ {
+ "wrapper": {
+ "name": "timeout-wrapper",
+ "wrappers": [
+ {
+ "timeout": {
+ "fail": true,
+ "elastic-percentage": 150,
+ "elastic-default-timeout": 90,
+ "type": "elastic"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "wrapper": {
+ "name": "pre-scm-shell-ant",
+ "wrappers": [
+ {
+ "pre-scm-buildstep": [
+ {
+ "shell": "#!/bin/bash\necho \"Doing somethiung cool\"\n"
+ },
+ {
+ "shell": "#!/bin/zsh\necho \"Doing somethin cool with zsh\"\n"
+ },
+ {
+ "ant": {
+ "targets": "target1 target2",
+ "ant-name": "Standard Ant"
+ }
+ },
+ {
+ "inject": {
+ "properties-file": "example.prop",
+ "properties-content": "EXAMPLE=foo-bar"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "wrapper": {
+ "name": "copy-files",
+ "wrappers": [
+ {
+ "copy-to-slave": {
+ "includes": [
+ "file1",
+ "file2*.txt"
+ ],
+ "excludes": [
+ "file2bad.txt"
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "trigger": {
+ "name": "gerrit-review",
+ "triggers": [
+ {
+ "gerrit": {
+ "trigger-on-patchset-uploaded-event": true,
+ "trigger-on-change-merged-event": false,
+ "trigger-on-ref-updated-event": false,
+ "trigger-on-comment-added-event": false,
+ "override-votes": true,
+ "gerrit-build-started-verified-value": 0,
+ "gerrit-build-successful-verified-value": 1,
+ "gerrit-build-failed-verified-value": -1,
+ "gerrit-build-unstable-verified-value": -1,
+ "gerrit-build-notbuilt-verified-value": -1,
+ "gerrit-build-started-codereview-value": 0,
+ "gerrit-build-successful-codereview-value": 1,
+ "gerrit-build-failed-codereview-value": -1,
+ "gerrit-build-unstable-codereview-value": -1,
+ "gerrit-build-notbuilt-codereview-value": -1,
+ "projects": [
+ {
+ "project-compare-type": "PLAIN",
+ "project-pattern": "{project_pattern}",
+ "branches": [
+ {
+ "branch-compare-type": "ANT",
+ "branch-pattern": "**"
+ }
+ ]
+ }
+ ],
+ "start-message": "Start message.",
+ "failure-message": "Failed message.",
+ "successful-message": "Success message.",
+ "unstable-message": "Unstable message.",
+ "notbuilt-message": "Not built message."
+ }
+ }
+ ]
+ }
+ },
+ {
+ "scm": {
+ "name": "gerrit-scm",
+ "scm": [
+ {
+ "git": {
+ "url": "ssh://jenkins@review.openstack.org:29418/{project_pattern}.git",
+ "branches": [
+ "origin/**"
+ ],
+ "name": "origin",
+ "prune": true,
+ "clean": true,
+ "browser": "gitweb",
+ "browser-url": "http://review.openstack.org/gitweb?p={project_pattern}.git",
+ "choosing-strategy": "gerrit"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "project": {
+ "name": "complete001",
+ "version": [
+ 1.2
+ ],
+ "jobs": [
+ "complete001_{version}"
+ ]
+ }
+ },
+ {
+ "job-template": {
+ "name": "complete001_{version}",
+ "scm": [
+ {
+ "gerrit-scm": {
+ "project_pattern": "openstack-infra/jenkins-job-builder"
+ }
+ }
+ ],
+ "triggers": [
+ {
+ "gerrit-review": {
+ "project_pattern": "openstack-infra/jenkins-job-builder"
+ }
+ }
+ ],
+ "wrappers": [
+ "timeout-wrapper",
+ "pre-scm-shell-ant",
+ "copy-files"
+ ],
+ "builders": [
+ {
+ "shell": "#!/usr/bin/env python\n#\nprint(\"Doing something cool with python\")\n"
+ }
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/jsonparser/fixtures/complete001.xml b/tests/jsonparser/fixtures/complete001.xml
new file mode 100644
index 000000000..f90b2f59f
--- /dev/null
+++ b/tests/jsonparser/fixtures/complete001.xml
@@ -0,0 +1,157 @@
+
+
+
+ <!-- Managed by Jenkins Job Builder -->
+ false
+ false
+ false
+ false
+ true
+
+
+ 2
+
+
+ origin
+ +refs/heads/*:refs/remotes/origin/*
+ ssh://jenkins@review.openstack.org:29418/openstack-infra/jenkins-job-builder.git
+
+
+
+
+ origin/**
+
+
+
+
+ false
+ false
+ false
+ false
+ true
+ true
+ false
+ Default
+
+
+
+
+
+ false
+
+ false
+ false
+
+
+
+
+
+ http://review.openstack.org/gitweb?p=openstack-infra/jenkins-job-builder.git
+
+
+
+
+
+
+
+ PLAIN
+ openstack-infra/jenkins-job-builder
+
+
+ ANT
+ **
+
+
+
+
+
+ false
+ false
+ false
+ false
+
+ false
+ false
+ true
+ false
+ false
+
+
+ False
+
+ false
+
+
+
+ 0
+ 1
+ -1
+ -1
+ -1
+ 0
+ 1
+ -1
+ -1
+ -1
+ Start message.
+ Failed message.
+ Success message.
+ Unstable message.
+ Not built message.
+
+
+ __ANY__
+
+
+
+
+ #!/usr/bin/env python
+#
+print("Doing something cool with python")
+
+
+
+
+
+
+ 3
+ true
+ false
+ 150
+ 90
+ elastic
+
+
+
+
+ #!/bin/bash
+echo "Doing somethiung cool"
+
+
+
+ #!/bin/zsh
+echo "Doing somethin cool with zsh"
+
+
+
+ target1 target2
+ Standard Ant
+
+
+
+ example.prop
+ EXAMPLE=foo-bar
+
+
+
+
+
+ file1,file2*.txt
+ file2bad.txt
+ false
+ false
+ userContent
+ false
+
+
+
diff --git a/tests/jsonparser/fixtures/simple.json b/tests/jsonparser/fixtures/simple.json
new file mode 100644
index 000000000..a47fe8db7
--- /dev/null
+++ b/tests/jsonparser/fixtures/simple.json
@@ -0,0 +1,13 @@
+[
+ {
+ "job": {
+ "name": "simple",
+ "node": "centos",
+ "builders": [
+ {
+ "shell": "echo This is my job"
+ }
+ ]
+ }
+ }
+]
\ No newline at end of file
diff --git a/tests/jsonparser/fixtures/simple.xml b/tests/jsonparser/fixtures/simple.xml
new file mode 100644
index 000000000..44d6abcfa
--- /dev/null
+++ b/tests/jsonparser/fixtures/simple.xml
@@ -0,0 +1,20 @@
+
+
+
+ <!-- Managed by Jenkins Job Builder -->
+ false
+ false
+ false
+ false
+ centos
+ false
+
+
+
+
+ echo This is my job
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/jsonparser/test_jsonparser.py b/tests/jsonparser/test_jsonparser.py
new file mode 100644
index 000000000..7b55e0a70
--- /dev/null
+++ b/tests/jsonparser/test_jsonparser.py
@@ -0,0 +1,27 @@
+# Joint copyright:
+# - Copyright 2012,2013 Wikimedia Foundation
+# - Copyright 2012,2013 Antoine "hashar" Musso
+# - Copyright 2013 Arnaud Fabre
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import os
+from testtools import TestCase
+from testscenarios.testcase import TestWithScenarios
+from tests.base import get_scenarios, SingleJobTestCase
+
+
+class TestCaseModuleJsonParser(TestWithScenarios, TestCase,
+ SingleJobTestCase):
+ fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures')
+ scenarios = get_scenarios(fixtures_path, in_ext='json', out_ext='xml')