use the cache file instead of scanner when possible
Define a loader API on top of the cache and scanner APIs and update the list, report, and sphinxext modules to use that instead of calling the scanner directly. Change-Id: I2899c2ae9bb46919a375ffe4f195b239cff389ef Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
parent
0b459b8337
commit
9cb8c4bf1b
|
@ -53,3 +53,4 @@ ChangeLog
|
|||
.*.swp
|
||||
.*sw?
|
||||
/cover/
|
||||
/releasenotes/notes/reno.cache
|
||||
|
|
|
@ -12,10 +12,6 @@
|
|||
|
||||
from __future__ import print_function
|
||||
|
||||
from reno import scanner
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
_SECTION_ORDER = [
|
||||
('features', 'New Features'),
|
||||
|
@ -41,7 +37,7 @@ def _indent_for_list(text, prefix=' '):
|
|||
]) + '\n'
|
||||
|
||||
|
||||
def format_report(reporoot, scanner_output, versions_to_include, title=None):
|
||||
def format_report(loader, versions_to_include, title=None):
|
||||
report = []
|
||||
if title:
|
||||
report.append('=' * len(title))
|
||||
|
@ -52,14 +48,9 @@ def format_report(reporoot, scanner_output, versions_to_include, title=None):
|
|||
# Read all of the notes files.
|
||||
file_contents = {}
|
||||
for version in versions_to_include:
|
||||
for filename, sha in scanner_output[version]:
|
||||
body = scanner.get_file_at_commit(
|
||||
reporoot,
|
||||
filename,
|
||||
sha,
|
||||
)
|
||||
y = yaml.safe_load(body)
|
||||
file_contents[filename] = y
|
||||
for filename, sha in loader[version]:
|
||||
body = loader.parse_note_file(filename, sha)
|
||||
file_contents[filename] = body
|
||||
|
||||
for version in versions_to_include:
|
||||
report.append(version)
|
||||
|
@ -67,7 +58,7 @@ def format_report(reporoot, scanner_output, versions_to_include, title=None):
|
|||
report.append('')
|
||||
|
||||
# Add the preludes.
|
||||
notefiles = scanner_output[version]
|
||||
notefiles = loader[version]
|
||||
for n, sha in notefiles:
|
||||
if 'prelude' in file_contents[n]:
|
||||
report.append(file_contents[n]['prelude'])
|
||||
|
|
|
@ -14,7 +14,7 @@ from __future__ import print_function
|
|||
|
||||
import logging
|
||||
|
||||
from reno import scanner
|
||||
from reno import loader
|
||||
from reno import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -26,17 +26,19 @@ def list_cmd(args):
|
|||
reporoot = args.reporoot.rstrip('/') + '/'
|
||||
notesdir = utils.get_notes_dir(args)
|
||||
collapse = args.collapse_pre_releases
|
||||
notes = scanner.get_notes_by_version(
|
||||
reporoot, notesdir, args.branch,
|
||||
ldr = loader.Loader(
|
||||
reporoot=reporoot,
|
||||
notesdir=notesdir,
|
||||
branch=args.branch,
|
||||
collapse_pre_releases=collapse,
|
||||
earliest_version=args.earliest_version,
|
||||
)
|
||||
if args.version:
|
||||
versions = args.version
|
||||
else:
|
||||
versions = notes.keys()
|
||||
versions = ldr.versions
|
||||
for version in versions:
|
||||
notefiles = notes[version]
|
||||
notefiles = ldr[version]
|
||||
print(version)
|
||||
for n, sha in notefiles:
|
||||
if n.startswith(reporoot):
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
from reno import scanner
|
||||
|
||||
import yaml
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_cache_filename(reporoot, notesdir):
|
||||
return os.path.join(reporoot, notesdir, 'reno.cache')
|
||||
|
||||
|
||||
class Loader(object):
|
||||
"Load the release notes for a given repository."
|
||||
|
||||
def __init__(self, reporoot, notesdir, branch=None,
|
||||
collapse_pre_releases=True,
|
||||
earliest_version=None,
|
||||
ignore_cache=False):
|
||||
"""Initialize a Loader.
|
||||
|
||||
The versions are presented in reverse chronological order.
|
||||
|
||||
Notes files are associated with the earliest version for which
|
||||
they were available, regardless of whether they changed later.
|
||||
|
||||
:param reporoot: Path to the root of the git repository.
|
||||
:type reporoot: str
|
||||
:param notesdir: The directory under *reporoot* with the release notes.
|
||||
:type notesdir: str
|
||||
:param branch: The name of the branch to scan. Defaults to current.
|
||||
:type branch: str
|
||||
:param collapse_pre_releases: When true, merge pre-release versions
|
||||
into the final release, if it is present.
|
||||
:type collapse_pre_releases: bool
|
||||
:param earliest_version: The oldest version to include.
|
||||
:type earliest_version: str
|
||||
:param ignore_cache: Do not load a cache file if it is present.
|
||||
:type ignore_cache: bool
|
||||
|
||||
"""
|
||||
self._reporoot = reporoot
|
||||
self._notesdir = notesdir
|
||||
self._branch = branch
|
||||
self._collapse_pre_releases = collapse_pre_releases
|
||||
self._earliest_version = earliest_version
|
||||
self._ignore_cache = ignore_cache
|
||||
|
||||
self._cache = None
|
||||
self._scanner_output = None
|
||||
self._cache_filename = get_cache_filename(reporoot, notesdir)
|
||||
|
||||
self._load_data()
|
||||
|
||||
def _load_data(self):
|
||||
cache_file_exists = os.path.exists(self._cache_filename)
|
||||
|
||||
if self._ignore_cache and cache_file_exists:
|
||||
LOG.debug('ignoring cache file %s', self._cache_filename)
|
||||
|
||||
if (not self._ignore_cache) and cache_file_exists:
|
||||
with open(self._cache_filename, 'r') as f:
|
||||
self._cache = yaml.safe_load(f.read())
|
||||
# Save the cached scanner output to the same attribute
|
||||
# it would be in if we had loaded it "live". This
|
||||
# simplifies some of the logic in the other methods.
|
||||
self._scanner_output = {
|
||||
n['version']: n['files']
|
||||
for n in self._cache['notes']
|
||||
}
|
||||
else:
|
||||
self._scanner_output = scanner.get_notes_by_version(
|
||||
reporoot=self._reporoot,
|
||||
notesdir=self._notesdir,
|
||||
branch=self._branch,
|
||||
collapse_pre_releases=self._collapse_pre_releases,
|
||||
earliest_version=self._earliest_version,
|
||||
)
|
||||
|
||||
@property
|
||||
def versions(self):
|
||||
"A list of all of the versions found."
|
||||
return list(self._scanner_output.keys())
|
||||
|
||||
def __getitem__(self, version):
|
||||
"Return data about the files that should go into a given version."
|
||||
return self._scanner_output[version]
|
||||
|
||||
def parse_note_file(self, filename, sha):
|
||||
"Return the data structure encoded in the note file."
|
||||
if self._cache:
|
||||
return self._cache['file-contents'][filename]
|
||||
else:
|
||||
body = scanner.get_file_at_commit(self._reporoot, filename, sha)
|
||||
return yaml.safe_load(body)
|
|
@ -39,6 +39,10 @@ _query_args = [
|
|||
(('--earliest-version',),
|
||||
dict(default=None,
|
||||
help='stop when this version is reached in the history')),
|
||||
(('--ignore-cache',),
|
||||
dict(default=False,
|
||||
action='store_true',
|
||||
help='if there is a cache file present, do not use it')),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
from __future__ import print_function
|
||||
|
||||
from reno import formatter
|
||||
from reno import scanner
|
||||
from reno import loader
|
||||
from reno import utils
|
||||
|
||||
|
||||
|
@ -22,18 +22,19 @@ def report_cmd(args):
|
|||
reporoot = args.reporoot.rstrip('/') + '/'
|
||||
notesdir = utils.get_notes_dir(args)
|
||||
collapse = args.collapse_pre_releases
|
||||
notes = scanner.get_notes_by_version(
|
||||
reporoot, notesdir, args.branch,
|
||||
ldr = loader.Loader(
|
||||
reporoot=reporoot,
|
||||
notesdir=notesdir,
|
||||
branch=args.branch,
|
||||
collapse_pre_releases=collapse,
|
||||
earliest_version=args.earliest_version,
|
||||
)
|
||||
if args.version:
|
||||
versions = args.version
|
||||
else:
|
||||
versions = notes.keys()
|
||||
versions = ldr.versions
|
||||
text = formatter.format_report(
|
||||
reporoot,
|
||||
notes,
|
||||
ldr,
|
||||
versions,
|
||||
title='Release Notes',
|
||||
)
|
||||
|
|
|
@ -14,7 +14,7 @@ import os.path
|
|||
|
||||
from reno import defaults
|
||||
from reno import formatter
|
||||
from reno import scanner
|
||||
from reno import loader
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers import rst
|
||||
|
@ -59,8 +59,10 @@ class ReleaseNotesDirective(rst.Directive):
|
|||
info('scanning %s for %s release notes' %
|
||||
(os.path.join(reporoot, notesdir), branch or 'current branch'))
|
||||
|
||||
notes = scanner.get_notes_by_version(
|
||||
reporoot, notesdir, branch,
|
||||
ldr = loader.Loader(
|
||||
reporoot=reporoot,
|
||||
notesdir=notesdir,
|
||||
branch=branch,
|
||||
collapse_pre_releases=collapse,
|
||||
earliest_version=earliest_version,
|
||||
)
|
||||
|
@ -70,10 +72,9 @@ class ReleaseNotesDirective(rst.Directive):
|
|||
for v in version_opt.split(',')
|
||||
]
|
||||
else:
|
||||
versions = notes.keys()
|
||||
versions = ldr.versions
|
||||
text = formatter.format_report(
|
||||
reporoot,
|
||||
notes,
|
||||
ldr,
|
||||
versions,
|
||||
title=title,
|
||||
)
|
||||
|
|
|
@ -12,12 +12,11 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import textwrap
|
||||
|
||||
from reno import formatter
|
||||
from reno import loader
|
||||
from reno.tests import base
|
||||
|
||||
from oslotest import mockpatch
|
||||
import mock
|
||||
|
||||
|
||||
class TestFormatter(base.TestCase):
|
||||
|
@ -30,19 +29,20 @@ class TestFormatter(base.TestCase):
|
|||
versions = ['0.0.0', '1.0.0']
|
||||
|
||||
note_bodies = {
|
||||
'note1': textwrap.dedent("""
|
||||
prelude: >
|
||||
This is the prelude.
|
||||
"""),
|
||||
'note2': textwrap.dedent("""
|
||||
issues:
|
||||
- This is the first issue.
|
||||
- This is the second issue.
|
||||
"""),
|
||||
'note3': textwrap.dedent("""
|
||||
features:
|
||||
- We added a feature!
|
||||
""")
|
||||
'note1': {
|
||||
'prelude': 'This is the prelude.',
|
||||
},
|
||||
'note2': {
|
||||
'issues': [
|
||||
'This is the first issue.',
|
||||
'This is the second issue.',
|
||||
],
|
||||
},
|
||||
'note3': {
|
||||
'features': [
|
||||
'We added a feature!',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def _get_note_body(self, reporoot, filename, sha):
|
||||
|
@ -50,15 +50,26 @@ class TestFormatter(base.TestCase):
|
|||
|
||||
def setUp(self):
|
||||
super(TestFormatter, self).setUp()
|
||||
self.useFixture(
|
||||
mockpatch.Patch('reno.scanner.get_file_at_commit',
|
||||
new=self._get_note_body)
|
||||
)
|
||||
|
||||
def _load(ldr):
|
||||
ldr._scanner_output = self.scanner_output
|
||||
ldr._cache = {
|
||||
'file-contents': self.note_bodies
|
||||
}
|
||||
|
||||
with mock.patch('reno.loader.Loader._load_data', _load):
|
||||
self.ldr = loader.Loader(
|
||||
reporoot='reporoot',
|
||||
notesdir='notesdir',
|
||||
branch=None,
|
||||
collapse_pre_releases=None,
|
||||
earliest_version=None,
|
||||
ignore_cache=False,
|
||||
)
|
||||
|
||||
def test_with_title(self):
|
||||
result = formatter.format_report(
|
||||
reporoot=None,
|
||||
scanner_output=self.scanner_output,
|
||||
loader=self.ldr,
|
||||
versions_to_include=self.versions,
|
||||
title='This is the title',
|
||||
)
|
||||
|
@ -66,8 +77,7 @@ class TestFormatter(base.TestCase):
|
|||
|
||||
def test_versions(self):
|
||||
result = formatter.format_report(
|
||||
reporoot=None,
|
||||
scanner_output=self.scanner_output,
|
||||
loader=self.ldr,
|
||||
versions_to_include=self.versions,
|
||||
title='This is the title',
|
||||
)
|
||||
|
@ -76,8 +86,7 @@ class TestFormatter(base.TestCase):
|
|||
|
||||
def test_without_title(self):
|
||||
result = formatter.format_report(
|
||||
reporoot=None,
|
||||
scanner_output=self.scanner_output,
|
||||
loader=self.ldr,
|
||||
versions_to_include=self.versions,
|
||||
title=None,
|
||||
)
|
||||
|
@ -85,8 +94,7 @@ class TestFormatter(base.TestCase):
|
|||
|
||||
def test_section_order(self):
|
||||
result = formatter.format_report(
|
||||
reporoot=None,
|
||||
scanner_output=self.scanner_output,
|
||||
loader=self.ldr,
|
||||
versions_to_include=self.versions,
|
||||
title=None,
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue