Chromium Code Reviews| Index: scripts/slave/gatekeeper_ng.py |
| diff --git a/scripts/slave/gatekeeper_ng.py b/scripts/slave/gatekeeper_ng.py |
| index b9cd7868ea448d978f9e405aaf6f4ff75b0955f8..4eaad8245d043f8cf5321f966f045e38d7ce4479 100755 |
| --- a/scripts/slave/gatekeeper_ng.py |
| +++ b/scripts/slave/gatekeeper_ng.py |
| @@ -153,6 +153,7 @@ def check_builds(master_builds, master_jsons, build_db, gatekeeper_config): |
| closing_steps = set(gatekeeper.get('closing_steps', [])) | forgiving |
| tree_notify = set(gatekeeper.get('tree_notify', [])) |
| sheriff_classes = set(gatekeeper.get('sheriff_classes', [])) |
| + subject_template = gatekeeper['subject_template'] |
| finished = [s for s in steps if s.get('isFinished')] |
| close_tree = gatekeeper.get('close_tree', True) |
| respect_build_status = gatekeeper.get('respect_build_status', False) |
| @@ -201,6 +202,7 @@ def check_builds(master_builds, master_jsons, build_db, gatekeeper_config): |
| 'forgiving_steps': forgiving, |
| 'project_name': project_name, |
| 'sheriff_classes': sheriff_classes, |
| + 'subject_template': subject_template, |
| 'tree_notify': tree_notify, |
| 'unsatisfied': unsatisfied_steps, |
| }) |
| @@ -243,6 +245,18 @@ def load_gatekeeper_config(filename): |
| instead of closing the tree. A section or builder can also specify to respect |
| a build's failure status with respect_build_status. |
| + The 'subject_template' key is the template used for the email subjects. Its |
| + formatting arguments are found at https://chromium.googlesource.com/chromium/ |
| + tools/chromium-build/+/master/gatekeeper_mailer.py, but the list is |
| + reproduced here: |
| + |
| + %(result)s: 'warning' or 'failure' |
| + %(projectName): 'Chromium', 'Chromium Perf', etc. |
| + %(builder): the builder name |
| + %(reason): reason for launching the build |
| + %(revision): build revision |
| + %(buildnumber): buildnumber |
| + |
| The 'comment' key can be put anywhere and is ignored by the parser. |
| # Python, not JSON. |
| @@ -275,6 +289,7 @@ def load_gatekeeper_config(filename): |
| }, |
| 'win_extra': { |
| 'closing_steps': ['extra_win_step'] |
| + 'subject_template': 'windows heads up on %(builder)', |
| } |
| } |
| } |
| @@ -289,9 +304,22 @@ def load_gatekeeper_config(filename): |
| Again, fields are optional and treated as empty lists/sets if not present. |
| """ |
| - master_keys = ['sheriff_classes', 'tree_notify'] |
| + # Keys which are allowed in a master or builder section. |
| + master_keys = ['sheriff_classes', 'tree_notify', 'subject_template'] |
| builder_keys = ['forgiving_steps', 'closing_steps', 'tree_notify', |
| - 'sheriff_classes'] |
| + 'sheriff_classes', 'subject_template'] |
| + |
| + |
| + # Keys which have defaults besides None or set([]). |
| + defaults = { |
| + 'subject_template': ('buildbot %(result)s in %(projectName)s on ' |
| + '%(builder)s, revision %(revision)s'), |
| + } |
| + |
| + # These keys are strings instead of sets. Strings can't be merged, |
| + # so more specific (master -> category -> builder) strings clobber |
| + # more generic ones. |
| + strings = ['subject_template'] |
| with open(filename) as f: |
| raw_gatekeeper_config = json.load(f) |
| @@ -314,20 +342,32 @@ def load_gatekeeper_config(filename): |
| builders = master_section.get('builders', {}) |
| for buildername, builder in builders.iteritems(): |
| allowed_keys(builder, 'categories', *builder_keys) |
| - for item in builder.values(): |
| - assert isinstance(item, list) |
| - assert all(isinstance(elem, basestring) for elem in item) |
| + for key, item in builder.iteritems(): |
| + if key in strings: |
| + assert isinstance(item, basestring) |
| + else: |
| + assert isinstance(item, list) |
| + assert all(isinstance(elem, basestring) for elem in item) |
| gatekeeper_config[master_url][-1].setdefault(buildername, {}) |
| gatekeeper_builder = gatekeeper_config[master_url][-1][buildername] |
| - # Populate with empty defaults. |
| + # Populate with specified defaults. |
| for k in builder_keys: |
| - gatekeeper_builder.setdefault(k, set()) |
| + if k in defaults: |
| + gatekeeper_builder.setdefault(k, defaults[k]) |
| + elif k in strings: |
| + gatekeeper_builder.setdefault(k, '') |
| + else: |
| + gatekeeper_builder.setdefault(k, set()) |
| # Inherit any values from the master. |
| for k in master_keys: |
| - gatekeeper_builder[k] |= set(master_section.get(k, [])) |
| + if k in strings: |
| + if k in master_section: |
| + gatekeeper_builder[k] = master_section[k] |
| + else: |
| + gatekeeper_builder[k] |= set(master_section.get(k, [])) |
| gatekeeper_builder['close_tree'] = master_section.get('close_tree', |
| True) |
| @@ -339,11 +379,19 @@ def load_gatekeeper_config(filename): |
| master_section.get( 'categories', [])) |
| for c in all_categories: |
| for k in builder_keys: |
| - gatekeeper_builder[k] |= set(categories[c].get(k, [])) |
| + if k in strings: |
| + if k in categories[c]: |
| + gatekeeper_builder[k] = categories[c][k] |
| + else: |
| + gatekeeper_builder[k] |= set(categories[c].get(k, [])) |
| # Add in any builder-specific values. |
| for k in builder_keys: |
| - gatekeeper_builder[k] |= set(builder.get(k, [])) |
| + if k in strings: |
| + if k in builder: |
| + gatekeeper_builder[k] = builder[k] |
| + else: |
| + gatekeeper_builder[k] |= set(builder.get(k, [])) |
| return gatekeeper_config |
|
iannucci
2013/12/17 00:46:23
This whole function is pretty funky. It may be wor
ghost stip (do not use)
2013/12/17 18:37:14
Good point, might make it a bit easier to traverse
|
| @@ -449,11 +497,11 @@ def close_tree_if_failure(failed_builds, username, password, tree_status_url, |
| sheriffs = get_sheriffs(failed_build['sheriff_classes'], sheriff_url) |
| watchers = list(tree_notify | blamelist | sheriffs) |
| - |
| build_data = { |
| 'build_url': build_url, |
| 'from_addr': fromaddr, |
| 'project_name': project_name, |
| + 'subject_template': failed_build['subject_template'], |
| 'steps': [], |
| 'unsatisfied': list(failed_build['unsatisfied']), |
| 'waterfall_url': waterfall_url, |