Merge "Ensure dict orders are deterministic"
This commit is contained in:
commit
32e2912418
|
@ -54,16 +54,58 @@ Example:
|
|||
"""
|
||||
|
||||
import codecs
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from ordereddict import OrderedDict
|
||||
import functools
|
||||
import logging
|
||||
import re
|
||||
import os
|
||||
import yaml
|
||||
from yaml.constructor import BaseConstructor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LocalLoader(yaml.Loader):
|
||||
class OrderedConstructor(BaseConstructor):
|
||||
"""The default constructor class for PyYAML loading uses standard python
|
||||
dictionaries which can have randomized ordering enabled (default in
|
||||
CPython from version 3.3). The order of the XML elements being outputted
|
||||
is both important for tests and for ensuring predictable generation based
|
||||
on the source. This subclass overrides this behaviour to ensure that all
|
||||
dict's created make use of OrderedDict to have iteration of keys to always
|
||||
follow the order in which the keys were inserted/created.
|
||||
"""
|
||||
|
||||
def construct_yaml_map(self, node):
|
||||
data = OrderedDict()
|
||||
yield data
|
||||
value = self.construct_mapping(node)
|
||||
|
||||
if isinstance(node, yaml.MappingNode):
|
||||
self.flatten_mapping(node)
|
||||
else:
|
||||
raise yaml.constructor.ConstructorError(
|
||||
None, None,
|
||||
'expected a mapping node, but found %s' % node.id,
|
||||
node.start_mark)
|
||||
|
||||
mapping = OrderedDict()
|
||||
for key_node, value_node in node.value:
|
||||
key = self.construct_object(key_node, deep=False)
|
||||
try:
|
||||
hash(key)
|
||||
except TypeError as exc:
|
||||
raise yaml.constructor.ConstructorError(
|
||||
'while constructing a mapping', node.start_mark,
|
||||
'found unacceptable key (%s)' % exc, key_node.start_mark)
|
||||
value = self.construct_object(value_node, deep=False)
|
||||
mapping[key] = value
|
||||
data.update(mapping)
|
||||
|
||||
|
||||
class LocalLoader(OrderedConstructor, yaml.Loader):
|
||||
"""Subclass for yaml.Loader which handles the local tags 'include',
|
||||
'include-raw' and 'include-raw-escaped' to specify a file to include data
|
||||
from and whether to parse it as additional yaml, treat it as a data blob
|
||||
|
@ -116,6 +158,11 @@ class LocalLoader(yaml.Loader):
|
|||
self.add_constructor('!include-raw-escape',
|
||||
self._include_raw_escape_tag)
|
||||
|
||||
# constructor to preserve order of maps and ensure that the order of
|
||||
# keys returned is consistent across multiple python versions
|
||||
self.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
|
||||
type(self).construct_yaml_map)
|
||||
|
||||
if isinstance(self.stream, file):
|
||||
self.search_path.add(os.path.normpath(
|
||||
os.path.dirname(self.stream.name)))
|
||||
|
|
|
@ -2399,7 +2399,7 @@ postbuildscript003.yaml
|
|||
'generic': 'GenericScript',
|
||||
'groovy': 'GroovyScriptFile',
|
||||
}
|
||||
for script_type in script_types.keys():
|
||||
for script_type in sorted(script_types.keys()):
|
||||
if script_type + '-script' not in data:
|
||||
continue
|
||||
|
||||
|
|
|
@ -107,14 +107,14 @@ def build_gerrit_triggers(xml_parent, data):
|
|||
|
||||
|
||||
def build_gerrit_skip_votes(xml_parent, data):
|
||||
outcomes = {'successful': 'onSuccessful',
|
||||
'failed': 'onFailed',
|
||||
'unstable': 'onUnstable',
|
||||
'notbuilt': 'onNotBuilt'}
|
||||
outcomes = [('successful', 'onSuccessful'),
|
||||
('failed', 'onFailed'),
|
||||
('unstable', 'onUnstable'),
|
||||
('notbuilt', 'onNotBuilt')]
|
||||
|
||||
skip_vote_node = XML.SubElement(xml_parent, 'skipVote')
|
||||
skip_vote = data.get('skip-vote', {})
|
||||
for result_kind, tag_name in outcomes.iteritems():
|
||||
for result_kind, tag_name in outcomes:
|
||||
if skip_vote.get(result_kind, False):
|
||||
XML.SubElement(skip_vote_node, tag_name).text = 'true'
|
||||
else:
|
||||
|
|
|
@ -2,6 +2,7 @@ hacking>=0.5.6,<0.8
|
|||
coverage>=3.6
|
||||
discover
|
||||
fixtures
|
||||
ordereddict
|
||||
python-subunit
|
||||
sphinx>=1.1.2,<1.2
|
||||
setuptools_git>=0.4
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
<hudson.tasks.Ant>
|
||||
<buildFile>build.xml</buildFile>
|
||||
<antOpts>-ea -Xmx512m</antOpts>
|
||||
<properties>failonerror=True
|
||||
builddir=/tmp/
|
||||
<properties>builddir=/tmp/
|
||||
failonerror=True
|
||||
</properties>
|
||||
<targets>debug test install</targets>
|
||||
<antName>Standard Ant</antName>
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
[
|
||||
{
|
||||
"template-job": {
|
||||
"name": "test-job-include-raw-{num}",
|
||||
"builders": [
|
||||
{
|
||||
"shell": "#!/bin/bash\n#\n# Sample script showing how the yaml include-raw tag can be used\n# to inline scripts that are maintained outside of the jenkins\n# job yaml configuration.\n\necho \"hello world\"\n\nexit 0\n"
|
||||
},
|
||||
{
|
||||
"shell": "#!/bin/bash\n#\n# sample script to check that brackets aren't escaped\n# when using the include-raw application yaml tag\n\nVAR1=\"hello\"\nVAR2=\"world\"\nVAR3=\"${{VAR1}} ${{VAR2}}\"\n\n[[ -n \"${{VAR3}}\" ]] && {{\n # this next section is executed as one\n echo \"${{VAR3}}\"\n exit 0\n}}\n\n"
|
||||
"shell": "#!/bin/bash\n#\n# sample script to check that brackets aren't escaped\n# when using the include-raw application yaml tag\n\nVAR1=\"hello\"\nVAR2=\"world\"\nVAR3=\"${{VAR1}} ${{VAR2}}\"\n\n[[ -n \"${{VAR3}}\" ]] && {{\n # this next section is executed as one\n echo \"${{VAR3}}\"\n exit 0\n}}\n\n"
|
||||
}
|
||||
],
|
||||
"name": "test-job-include-raw-{num}"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"project": {
|
||||
"name": "test-job-template-1",
|
||||
"num": 1,
|
||||
"jobs": [
|
||||
"test-job-include-raw-{num}"
|
||||
],
|
||||
"name": "test-job-template-1"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
[
|
||||
{
|
||||
"job": {
|
||||
"name": "test-job-include-raw-1",
|
||||
"builders": [
|
||||
{
|
||||
"shell": "#!/bin/bash\n#\n# Sample script showing how the yaml include-raw tag can be used\n# to inline scripts that are maintained outside of the jenkins\n# job yaml configuration.\n\necho \"hello world\"\n\nexit 0\n"
|
||||
},
|
||||
{
|
||||
"shell": "#!/bin/bash\n#\n# sample script to check that brackets aren't escaped\n# when using the include-raw application yaml tag\n\nVAR1=\"hello\"\nVAR2=\"world\"\nVAR3=\"${VAR1} ${VAR2}\"\n\n[[ -n \"${VAR3}\" ]] && {\n # this next section is executed as one\n echo \"${VAR3}\"\n exit 0\n}\n\n"
|
||||
"shell": "#!/bin/bash\n#\n# sample script to check that brackets aren't escaped\n# when using the include-raw application yaml tag\n\nVAR1=\"hello\"\nVAR2=\"world\"\nVAR3=\"${VAR1} ${VAR2}\"\n\n[[ -n \"${VAR3}\" ]] && {\n # this next section is executed as one\n echo \"${VAR3}\"\n exit 0\n}\n\n"
|
||||
}
|
||||
],
|
||||
"name": "test-job-include-raw-1"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,43 +1,43 @@
|
|||
[
|
||||
{
|
||||
"job": {
|
||||
"name": "test-job-1",
|
||||
"builders": [
|
||||
{
|
||||
"copyartifact": {
|
||||
"filter": "*.tar.gz",
|
||||
"project": "foo",
|
||||
"parameter-filters": "PUBLISH=true",
|
||||
"filter": "*.tar.gz",
|
||||
"target": "/home/foo",
|
||||
"flatten": true,
|
||||
"which-build": "last-successful",
|
||||
"optional": true,
|
||||
"which-build": "last-successful"
|
||||
"flatten": true,
|
||||
"parameter-filters": "PUBLISH=true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"copyartifact": {
|
||||
"project": "bar",
|
||||
"parameter-filters": "PUBLISH=true",
|
||||
"target": "/home/foo",
|
||||
"build-number": 123,
|
||||
"which-build": "specific-build",
|
||||
"filter": "*.tar.gz",
|
||||
"target": "/home/foo",
|
||||
"which-build": "specific-build",
|
||||
"optional": true,
|
||||
"flatten": true,
|
||||
"optional": true
|
||||
"parameter-filters": "PUBLISH=true",
|
||||
"build-number": 123
|
||||
}
|
||||
},
|
||||
{
|
||||
"copyartifact": {
|
||||
"filter": "*.tar.gz",
|
||||
"project": "baz",
|
||||
"parameter-filters": "PUBLISH=true",
|
||||
"filter": "*.tar.gz",
|
||||
"target": "/home/foo",
|
||||
"flatten": true,
|
||||
"which-build": "upstream-build",
|
||||
"optional": true,
|
||||
"which-build": "upstream-build"
|
||||
"flatten": true,
|
||||
"parameter-filters": "PUBLISH=true"
|
||||
}
|
||||
}
|
||||
],
|
||||
"name": "test-job-1"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -7,14 +7,14 @@
|
|||
<hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
|
||||
<metric>TOTAL_COVERAGE</metric>
|
||||
<healthy>80</healthy>
|
||||
<unstable>0</unstable>
|
||||
<unhealthy>0</unhealthy>
|
||||
<unstable>0</unstable>
|
||||
</hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
|
||||
<hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
|
||||
<metric>CODE_COVERAGE</metric>
|
||||
<healthy>80</healthy>
|
||||
<unstable>0</unstable>
|
||||
<unhealthy>0</unhealthy>
|
||||
<unstable>0</unstable>
|
||||
</hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
|
||||
</targets>
|
||||
</hudson.plugins.rubyMetrics.rcov.RcovPublisher>
|
||||
|
|
|
@ -18,15 +18,15 @@
|
|||
</types>
|
||||
<thresholds>
|
||||
<org.jenkinsci.plugins.xunit.threshold.FailedThreshold>
|
||||
<failureThreshold/>
|
||||
<unstableThreshold/>
|
||||
<unstableNewThreshold/>
|
||||
<failureThreshold/>
|
||||
<failureNewThreshold/>
|
||||
</org.jenkinsci.plugins.xunit.threshold.FailedThreshold>
|
||||
<org.jenkinsci.plugins.xunit.threshold.SkippedThreshold>
|
||||
<failureThreshold/>
|
||||
<unstableThreshold/>
|
||||
<unstableNewThreshold/>
|
||||
<failureThreshold/>
|
||||
<failureNewThreshold/>
|
||||
</org.jenkinsci.plugins.xunit.threshold.SkippedThreshold>
|
||||
</thresholds>
|
||||
|
|
Loading…
Reference in New Issue