make sections configurable
The list of section identifiers and corresponding display names, and the order in which they are rendered, was hard-coded. Some projects want to customise this, so move it into the Config object so that it can now be specified via config.yaml, e.g. sections: - [features, New Features] - [issues, Known Issues] - [upgrade, Upgrade Notes] - [api, API Changes] - [security, Security Issues] - [fixes, Bug Fixes] Change-Id: I914572c6a07ca81c54965b4b5a6b6aba50b3a787
This commit is contained in:
parent
10ccdda0eb
commit
081a4145e1
@ -50,13 +50,16 @@ Editing a Release Note
|
||||
======================
|
||||
|
||||
The note file is a YAML file with several sections. All of the text is
|
||||
interpreted as having `reStructuredText`_ formatting.
|
||||
interpreted as having `reStructuredText`_ formatting. The permitted
|
||||
sections are configurable (see below) but default to the following
|
||||
list:
|
||||
|
||||
prelude
|
||||
|
||||
General comments about the release. The prelude from all notes in a
|
||||
section are combined, in note order, to produce a single prelude
|
||||
introducing that release.
|
||||
introducing that release. This section is always included, regardless
|
||||
of what sections are configured.
|
||||
|
||||
features
|
||||
|
||||
@ -177,6 +180,14 @@ may be the most convenient way to manage the values consistently.
|
||||
earliest_version: 12.0.0
|
||||
collapse_pre_releases: false
|
||||
stop_at_branch_base: true
|
||||
sections:
|
||||
# The prelude section is implicitly included.
|
||||
- [features, New Features]
|
||||
- [issues, Known Issues]
|
||||
- [upgrade, Upgrade Notes]
|
||||
- [api, API Changes]
|
||||
- [security, Security Issues]
|
||||
- [fixes, Bug Fixes]
|
||||
template: |
|
||||
<template-used-to-create-new-notes>
|
||||
...
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add a configuration option ``sections`` to hold the list of
|
||||
permitted section identifiers and corresponding display names.
|
||||
This also determines the order in which sections are collated.
|
||||
|
@ -140,6 +140,20 @@ class Config(object):
|
||||
# scanning history to determine where to stop, to find the
|
||||
# "base" of a branch. Other branches are ignored.
|
||||
'branch_name_re': 'stable/.+',
|
||||
|
||||
# The identifiers and names of permitted sections in the
|
||||
# release notes, in the order in which the final report will
|
||||
# be generated.
|
||||
'sections': [
|
||||
['features', 'New Features'],
|
||||
['issues', 'Known Issues'],
|
||||
['upgrade', 'Upgrade Notes'],
|
||||
['deprecations', 'Deprecation Notes'],
|
||||
['critical', 'Critical Issues'],
|
||||
['security', 'Security Issues'],
|
||||
['fixes', 'Bug Fixes'],
|
||||
['other', 'Other Notes'],
|
||||
],
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
@ -13,18 +13,6 @@
|
||||
from __future__ import print_function
|
||||
|
||||
|
||||
_SECTION_ORDER = [
|
||||
('features', 'New Features'),
|
||||
('issues', 'Known Issues'),
|
||||
('upgrade', 'Upgrade Notes'),
|
||||
('deprecations', 'Deprecation Notes'),
|
||||
('critical', 'Critical Issues'),
|
||||
('security', 'Security Issues'),
|
||||
('fixes', 'Bug Fixes'),
|
||||
('other', 'Other Notes'),
|
||||
]
|
||||
|
||||
|
||||
def _indent_for_list(text, prefix=' '):
|
||||
"""Indent some text to make it work as a list entry.
|
||||
|
||||
@ -37,7 +25,7 @@ def _indent_for_list(text, prefix=' '):
|
||||
]) + '\n'
|
||||
|
||||
|
||||
def format_report(loader, versions_to_include, title=None):
|
||||
def format_report(loader, config, versions_to_include, title=None):
|
||||
report = []
|
||||
if title:
|
||||
report.append('=' * len(title))
|
||||
@ -64,7 +52,7 @@ def format_report(loader, versions_to_include, title=None):
|
||||
report.append(file_contents[n]['prelude'])
|
||||
report.append('')
|
||||
|
||||
for section_name, section_title in _SECTION_ORDER:
|
||||
for section_name, section_title in config.sections:
|
||||
notes = [
|
||||
n
|
||||
for fn, sha in notefiles
|
||||
|
@ -25,6 +25,7 @@ def report_cmd(args, conf):
|
||||
versions = ldr.versions
|
||||
text = formatter.format_report(
|
||||
ldr,
|
||||
conf,
|
||||
versions,
|
||||
title='Release Notes',
|
||||
)
|
||||
|
@ -90,6 +90,7 @@ class ReleaseNotesDirective(rst.Directive):
|
||||
info('got versions %s' % (versions,))
|
||||
text = formatter.format_report(
|
||||
ldr,
|
||||
conf,
|
||||
versions,
|
||||
title=title,
|
||||
)
|
||||
|
@ -20,7 +20,7 @@ from reno import loader
|
||||
from reno.tests import base
|
||||
|
||||
|
||||
class TestFormatter(base.TestCase):
|
||||
class TestFormatterBase(base.TestCase):
|
||||
|
||||
scanner_output = {
|
||||
'0.0.0': [('note1', 'shaA')],
|
||||
@ -29,6 +29,29 @@ class TestFormatter(base.TestCase):
|
||||
|
||||
versions = ['0.0.0', '1.0.0']
|
||||
|
||||
def _get_note_body(self, reporoot, filename, sha):
|
||||
return self.note_bodies.get(filename, '')
|
||||
|
||||
def setUp(self):
|
||||
super(TestFormatterBase, self).setUp()
|
||||
|
||||
def _load(ldr):
|
||||
ldr._scanner_output = self.scanner_output
|
||||
ldr._cache = {
|
||||
'file-contents': self.note_bodies
|
||||
}
|
||||
|
||||
self.c = config.Config('reporoot')
|
||||
|
||||
with mock.patch('reno.loader.Loader._load_data', _load):
|
||||
self.ldr = loader.Loader(
|
||||
self.c,
|
||||
ignore_cache=False,
|
||||
)
|
||||
|
||||
|
||||
class TestFormatter(TestFormatterBase):
|
||||
|
||||
note_bodies = {
|
||||
'note1': {
|
||||
'prelude': 'This is the prelude.',
|
||||
@ -47,29 +70,10 @@ class TestFormatter(base.TestCase):
|
||||
},
|
||||
}
|
||||
|
||||
def _get_note_body(self, reporoot, filename, sha):
|
||||
return self.note_bodies.get(filename, '')
|
||||
|
||||
def setUp(self):
|
||||
super(TestFormatter, self).setUp()
|
||||
|
||||
def _load(ldr):
|
||||
ldr._scanner_output = self.scanner_output
|
||||
ldr._cache = {
|
||||
'file-contents': self.note_bodies
|
||||
}
|
||||
|
||||
self.c = config.Config('reporoot')
|
||||
|
||||
with mock.patch('reno.loader.Loader._load_data', _load):
|
||||
self.ldr = loader.Loader(
|
||||
self.c,
|
||||
ignore_cache=False,
|
||||
)
|
||||
|
||||
def test_with_title(self):
|
||||
result = formatter.format_report(
|
||||
loader=self.ldr,
|
||||
config=self.c,
|
||||
versions_to_include=self.versions,
|
||||
title='This is the title',
|
||||
)
|
||||
@ -78,6 +82,7 @@ class TestFormatter(base.TestCase):
|
||||
def test_versions(self):
|
||||
result = formatter.format_report(
|
||||
loader=self.ldr,
|
||||
config=self.c,
|
||||
versions_to_include=self.versions,
|
||||
title='This is the title',
|
||||
)
|
||||
@ -87,14 +92,16 @@ class TestFormatter(base.TestCase):
|
||||
def test_without_title(self):
|
||||
result = formatter.format_report(
|
||||
loader=self.ldr,
|
||||
config=self.c,
|
||||
versions_to_include=self.versions,
|
||||
title=None,
|
||||
)
|
||||
self.assertNotIn('This is the title', result)
|
||||
|
||||
def test_section_order(self):
|
||||
def test_default_section_order(self):
|
||||
result = formatter.format_report(
|
||||
loader=self.ldr,
|
||||
config=self.c,
|
||||
versions_to_include=self.versions,
|
||||
title=None,
|
||||
)
|
||||
@ -104,3 +111,48 @@ class TestFormatter(base.TestCase):
|
||||
expected = [prelude_pos, features_pos, issues_pos]
|
||||
actual = list(sorted([prelude_pos, features_pos, issues_pos]))
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
||||
class TestFormatterCustomSections(TestFormatterBase):
|
||||
note_bodies = {
|
||||
'note1': {
|
||||
'prelude': 'This is the prelude.',
|
||||
},
|
||||
'note2': {
|
||||
'features': [
|
||||
'This is the first feature.',
|
||||
],
|
||||
'api': [
|
||||
'This is the API change for the first feature.',
|
||||
],
|
||||
},
|
||||
'note3': {
|
||||
'api': [
|
||||
'This is the API change for the second feature.',
|
||||
],
|
||||
'features': [
|
||||
'This is the second feature.',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestFormatterCustomSections, self).setUp()
|
||||
self.c.override(sections=[
|
||||
['api', 'API Changes'],
|
||||
['features', 'New Features'],
|
||||
])
|
||||
|
||||
def test_custom_section_order(self):
|
||||
result = formatter.format_report(
|
||||
loader=self.ldr,
|
||||
config=self.c,
|
||||
versions_to_include=self.versions,
|
||||
title=None,
|
||||
)
|
||||
prelude_pos = result.index('This is the prelude.')
|
||||
api_pos = result.index('API Changes')
|
||||
features_pos = result.index('New Features')
|
||||
expected = [prelude_pos, api_pos, features_pos]
|
||||
actual = list(sorted([prelude_pos, features_pos, api_pos]))
|
||||
self.assertEqual(expected, actual)
|
||||
|
Loading…
Reference in New Issue
Block a user