loader: Handle note files missing top-level keys

Release note files should look like this:

  issues:
  - |
    Some text here.

However, it's possible to make a mistake and create a release note
missing the top-level key:

  - |
    Some text here.

If you do this, reno fails with a cryptic error which Sphinx will bubble
up with zero context:

  AttributeError: 'list' object has no attribute 'items'

Make this failure more obvious.

Change-Id: I488d14bc1134a38d4f76ed5f5368c84db8e39df3
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
Story: 1670421
Task: 42802
This commit is contained in:
Stephen Finucane 2021-07-09 11:39:03 +01:00
parent 7a82e8fe05
commit c2d9357803
3 changed files with 32 additions and 0 deletions

View File

@ -0,0 +1,5 @@
---
features:
- |
Release note file validation is improved. Files missing section information
will now be correctly handled and rejected.

View File

@ -119,6 +119,17 @@ class Loader(object):
cleaned_content = {}
if not isinstance(content, dict):
LOG.warning(
'%s does not appear to be structured as a YAML mapping. '
'Did you forget a top-level key?',
filename,
)
raise ValueError(
f'{filename} does not appear to be structured as a YAML '
f'mapping. Did you forget a top-level key?'
)
for section_name, section_content in content.items():
if section_name == self._config.prelude_section_name:
if not isinstance(section_content, str):

View File

@ -87,3 +87,19 @@ class TestValidate(base.TestCase):
ldr = self._make_loader(note_bodies)
ldr.parse_note_file('note1', None)
self.assertIn('dict', self.logger.output)
def test_invalid_note_with_missing_key(self):
"""Test behavior when note is not structured as a mapping.
This one should be an error since we can't correct the input.
"""
note_bodies = yaml.safe_load(textwrap.dedent('''
- |
This is an issue but we're missing the top-level 'issues' key.
'''))
self.assertIsInstance(note_bodies, list)
ldr = self._make_loader(note_bodies)
self.assertRaises(ValueError, ldr.parse_note_file, 'note1', None)
self.assertIn(
'does not appear to be structured as a YAML mapping',
self.logger.output)