Allow users to change prelude section name

Convert note template to a format string. Also include prelude
section in the report generated and update docs.

Closes-Bug: #1698203
Change-Id: I7bef68bfb518dd8554d56cb200f2844e7d395fc8
This commit is contained in:
Rajath Agasthya 2017-06-16 02:14:29 -07:00
parent b2aadef726
commit 4003fc1321
7 changed files with 151 additions and 99 deletions

View File

@ -56,8 +56,8 @@ list:
prelude prelude
General comments about the release. The prelude from all notes in a General comments about the release. Prelude sections from all notes in a
section are combined, in note order, to produce a single prelude release are combined, in note order, to produce a single prelude
introducing that release. This section is always included, regardless introducing that release. This section is always included, regardless
of what sections are configured. of what sections are configured.
@ -197,6 +197,9 @@ may be the most convenient way to manage the values consistently.
- [api, API Changes] - [api, API Changes]
- [security, Security Issues] - [security, Security Issues]
- [fixes, Bug Fixes] - [fixes, Bug Fixes]
# Change prelude_section_name to 'release_summary' from default value
# 'prelude'.
prelude_section_name: release_summary
template: | template: |
<template-used-to-create-new-notes> <template-used-to-create-new-notes>
... ...
@ -284,6 +287,15 @@ The following options are configurable:
order in which the final report will be generated. A prelude section will order in which the final report will be generated. A prelude section will
always be automatically inserted before the first element of this list. always be automatically inserted before the first element of this list.
`prelude_section_name`
The name of the prelude section in the note template. Note that the
value for this must be a single word, but can have underscores. The
value is displayed in titlecase in the report after replacing
underscores with spaces.
Defaults to ``prelude``
`ignore_null_merges` `ignore_null_merges`
OpenStack used to use null-merges to bring final release tags from OpenStack used to use null-merges to bring final release tags from

View File

@ -19,74 +19,6 @@ from reno import defaults
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
_TEMPLATE = """\
---
prelude: >
Replace this text with content to appear at the top of the section for this
release. All of the prelude content is merged together and then rendered
separately from the items listed in other parts of the file, so the text
needs to be worded so that both the prelude and the other items make sense
when read independently. This may mean repeating some details. Not every
release note requires a prelude. Usually only notes describing major
features or adding release theme details should have a prelude.
features:
- |
List new features here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
issues:
- |
List known issues here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
upgrade:
- |
List upgrade notes here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
deprecations:
- |
List deprecations notes here, or remove this section. All of the list
items in this section are combined when the release notes are rendered, so
the text needs to be worded so that it does not depend on any information
only available in another section, such as the prelude. This may mean
repeating some details.
critical:
- |
Add critical notes here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
security:
- |
Add security notes here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
fixes:
- |
Add normal bug fixes here, or remove this section. All of the list items
in this section are combined when the release notes are rendered, so the
text needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
other:
- |
Add other notes here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
"""
class Config(object): class Config(object):
@ -115,7 +47,7 @@ class Config(object):
'earliest_version': None, 'earliest_version': None,
# The template used by reno new to create a note. # The template used by reno new to create a note.
'template': _TEMPLATE, 'template': defaults.TEMPLATE.format(defaults.PRELUDE_SECTION_NAME),
# The RE pattern used to match the repo tags representing a valid # The RE pattern used to match the repo tags representing a valid
# release version. The pattern is compiled with the verbose and unicode # release version. The pattern is compiled with the verbose and unicode
@ -156,6 +88,13 @@ class Config(object):
['other', 'Other Notes'], ['other', 'Other Notes'],
], ],
# The name of the prelude section in the note template. This
# allows users to rename the section to, for example,
# 'release_summary' or 'project_wide_general_announcements',
# which is displayed in titlecase in the report after
# replacing underscores with spaces.
'prelude_section_name': defaults.PRELUDE_SECTION_NAME,
# When this option is set to True, any merge commits with no # When this option is set to True, any merge commits with no
# changes and in which the second or later parent is tagged # changes and in which the second or later parent is tagged
# are considered "null-merges" that bring the tag information # are considered "null-merges" that bring the tag information
@ -219,6 +158,13 @@ class Config(object):
else: else:
self.override(**self._contents) self.override(**self._contents)
def _rename_prelude_section(self, **kwargs):
key = 'prelude_section_name'
if key in kwargs and kwargs[key] != self._OPTS[key]:
new_prelude_name = kwargs[key]
self.template = defaults.TEMPLATE.format(new_prelude_name)
def override(self, **kwds): def override(self, **kwds):
"""Set the values of the named configuration options. """Set the values of the named configuration options.
@ -227,6 +173,9 @@ class Config(object):
present. present.
""" """
# Replace prelude section name if it has been changed.
self._rename_prelude_section(**kwds)
for n, v in kwds.items(): for n, v in kwds.items():
if n not in self._OPTS: if n not in self._OPTS:
LOG.warning('ignoring unknown configuration value %r = %r', LOG.warning('ignoring unknown configuration value %r = %r',
@ -268,6 +217,15 @@ class Config(object):
""" """
return os.path.join(self.relnotesdir, self.notesdir) return os.path.join(self.relnotesdir, self.notesdir)
@property
def options(self):
"""Get all configuration options as a dict.
Returns the actual configuration options after overrides.
"""
options = {o: getattr(self, o) for o in self._OPTS}
return options
# def parse_config_into(parsed_arguments): # def parse_config_into(parsed_arguments):
# """Parse the user config onto the namespace arguments. # """Parse the user config onto the namespace arguments.

View File

@ -12,3 +12,72 @@
RELEASE_NOTES_SUBDIR = 'releasenotes' RELEASE_NOTES_SUBDIR = 'releasenotes'
NOTES_SUBDIR = 'notes' NOTES_SUBDIR = 'notes'
PRELUDE_SECTION_NAME = 'prelude'
# This is a format string, so it needs to be formatted wherever it is used.
TEMPLATE = """\
---
{0}: >
Replace this text with content to appear at the top of the section for this
release. All of the prelude content is merged together and then rendered
separately from the items listed in other parts of the file, so the text
needs to be worded so that both the prelude and the other items make sense
when read independently. This may mean repeating some details. Not every
release note requires a prelude. Usually only notes describing major
features or adding release theme details should have a prelude.
features:
- |
List new features here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
issues:
- |
List known issues here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
upgrade:
- |
List upgrade notes here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
deprecations:
- |
List deprecations notes here, or remove this section. All of the list
items in this section are combined when the release notes are rendered, so
the text needs to be worded so that it does not depend on any information
only available in another section, such as the prelude. This may mean
repeating some details.
critical:
- |
Add critical notes here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
security:
- |
Add security notes here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
fixes:
- |
Add normal bug fixes here, or remove this section. All of the list items
in this section are combined when the release notes are rendered, so the
text needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
other:
- |
Add other notes here, or remove this section. All of the list items in
this section are combined when the release notes are rendered, so the text
needs to be worded so that it does not depend on any information only
available in another section, such as the prelude. This may mean repeating
some details.
"""

View File

@ -48,13 +48,21 @@ def format_report(loader, config, versions_to_include, title=None,
# Add the preludes. # Add the preludes.
notefiles = loader[version] notefiles = loader[version]
for n, sha in notefiles: prelude_name = config.prelude_section_name
if 'prelude' in file_contents[n]: notefiles_with_prelude = [(n, sha) for n, sha in notefiles
if show_source: if prelude_name in file_contents[n]]
report.append('.. %s @ %s\n' % (n, sha)) if notefiles_with_prelude:
report.append(file_contents[n]['prelude']) report.append(prelude_name.replace('_', ' ').title())
report.append('') report.append('-' * len(prelude_name))
report.append('')
for n, sha in notefiles_with_prelude:
if show_source:
report.append('.. %s @ %s\n' % (n, sha))
report.append(file_contents[n][prelude_name])
report.append('')
# Add other sections.
for section_name, section_title in config.sections: for section_name, section_title in config.sections:
notes = [ notes = [
(n, fn, sha) (n, fn, sha)

View File

@ -30,8 +30,8 @@ def lint_cmd(args, conf):
error = 0 error = 0
load = loader.Loader(conf, ignore_cache=True) load = loader.Loader(conf, ignore_cache=True)
allowed_section_names = [conf.prelude_section_name] + \
allowed_section_names = ['prelude'] + [s[0] for s in conf.sections] [s[0] for s in conf.sections]
uids = {} uids = {}
for f in notes: for f in notes:

View File

@ -104,13 +104,13 @@ class Loader(object):
cleaned_content = {} cleaned_content = {}
for section_name, section_content in content.items(): for section_name, section_content in content.items():
if section_name == 'prelude': if section_name == self._config.prelude_section_name:
if not isinstance(section_content, six.string_types): if not isinstance(section_content, six.string_types):
LOG.warning( LOG.warning(
('The prelude section of %s ' ('The %s section of %s '
'does not parse as a single string. ' 'does not parse as a single string. '
'Is the YAML input escaped properly?') % 'Is the YAML input escaped properly?') %
filename, (self._config.prelude_section_name, filename),
) )
else: else:
if isinstance(section_content, six.string_types): if isinstance(section_content, six.string_types):

View File

@ -17,6 +17,7 @@ import os
import fixtures import fixtures
from reno import config from reno import config
from reno import defaults
from reno import main from reno import main
from reno.tests import base from reno.tests import base
@ -35,10 +36,7 @@ collapse_pre_releases: false
def test_defaults(self): def test_defaults(self):
c = config.Config(self.tempdir.path) c = config.Config(self.tempdir.path)
actual = { actual = c.options
o: getattr(c, o)
for o in config.Config._OPTS.keys()
}
self.assertEqual(config.Config._OPTS, actual) self.assertEqual(config.Config._OPTS, actual)
def test_override(self): def test_override(self):
@ -46,10 +44,7 @@ collapse_pre_releases: false
c.override( c.override(
collapse_pre_releases=False, collapse_pre_releases=False,
) )
actual = { actual = c.options
o: getattr(c, o)
for o in config.Config._OPTS.keys()
}
expected = {} expected = {}
expected.update(config.Config._OPTS) expected.update(config.Config._OPTS)
expected['collapse_pre_releases'] = False expected['collapse_pre_releases'] = False
@ -63,10 +58,7 @@ collapse_pre_releases: false
c.override( c.override(
notesdir='value2', notesdir='value2',
) )
actual = { actual = c.options
o: getattr(c, o)
for o in config.Config._OPTS.keys()
}
expected = {} expected = {}
expected.update(config.Config._OPTS) expected.update(config.Config._OPTS)
expected['notesdir'] = 'value2' expected['notesdir'] = 'value2'
@ -119,10 +111,7 @@ collapse_pre_releases: false
c = self._run_override_from_parsed_args([ c = self._run_override_from_parsed_args([
'--no-collapse-pre-releases', '--no-collapse-pre-releases',
]) ])
actual = { actual = c.options
o: getattr(c, o)
for o in config.Config._OPTS.keys()
}
expected = {} expected = {}
expected.update(config.Config._OPTS) expected.update(config.Config._OPTS)
expected['collapse_pre_releases'] = False expected['collapse_pre_releases'] = False
@ -158,6 +147,22 @@ class TestConfigProperties(base.TestCase):
self.assertEqual('releasenotes/thenotes', self.c.notespath) self.assertEqual('releasenotes/thenotes', self.c.notespath)
def test_template(self): def test_template(self):
self.assertEqual(config._TEMPLATE, self.c.template) template = defaults.TEMPLATE.format(defaults.PRELUDE_SECTION_NAME)
self.assertEqual(template, self.c.template)
self.c.override(template='i-am-a-template') self.c.override(template='i-am-a-template')
self.assertEqual('i-am-a-template', self.c.template) self.assertEqual('i-am-a-template', self.c.template)
def test_prelude_override(self):
template = defaults.TEMPLATE.format(defaults.PRELUDE_SECTION_NAME)
self.assertEqual(template, self.c.template)
self.c.override(prelude_section_name='fake_prelude_name')
expected_template = defaults.TEMPLATE.format('fake_prelude_name')
self.assertEqual(expected_template, self.c.template)
def test_prelude_and_template_override(self):
template = defaults.TEMPLATE.format(defaults.PRELUDE_SECTION_NAME)
self.assertEqual(template, self.c.template)
self.c.override(prelude_section_name='fake_prelude_name',
template='i-am-a-template')
self.assertEqual('fake_prelude_name', self.c.prelude_section_name)
self.assertEqual('i-am-a-template', self.c.template)