refactor existing implementation into a class

As part of the re-implementation work using dulwich I want to create a
Scanner class, to replace some of the module-level functions in the
scanner module now. This patch moves existing code around into something
like what I would expect that API to look like, without changing the
implementation details.

The class API is still subject to change in the future, but this should
make it a little easier to review those implementation changes.

Change-Id: If92f6ff5bce955bbdcb748cee52ad71210ae313b
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
Doug Hellmann 2016-12-05 16:17:30 -05:00
parent 17a248dc84
commit db4e3647e2
5 changed files with 411 additions and 377 deletions

View File

@ -20,7 +20,8 @@ from reno import scanner
def build_cache_db(conf, versions_to_include):
notes = scanner.get_notes_by_version(conf)
s = scanner.Scanner(conf)
notes = s.get_notes_by_version()
# Default to including all versions returned by the scanner.
if not versions_to_include:
@ -31,11 +32,7 @@ def build_cache_db(conf, versions_to_include):
file_contents = {}
for version in versions_to_include:
for filename, sha in notes[version]:
body = scanner.get_file_at_commit(
conf.reporoot,
filename,
sha,
)
body = s.get_file_at_commit(filename, sha)
# We want to save the contents of the file, which is YAML,
# inside another YAML file. That looks terribly ugly with
# all of the escapes needed to format it properly as

View File

@ -52,6 +52,7 @@ class Loader(object):
self._earliest_version = conf.earliest_version
self._cache = None
self._scanner = scanner.Scanner(self._config)
self._scanner_output = None
self._cache_filename = get_cache_filename(self._reporoot,
self._notespath)
@ -75,7 +76,7 @@ class Loader(object):
for n in self._cache['notes']
}
else:
self._scanner_output = scanner.get_notes_by_version(self._config)
self._scanner_output = self._scanner.get_notes_by_version()
@property
def versions(self):
@ -96,7 +97,7 @@ class Loader(object):
if self._cache:
content = self._cache['file-contents'][filename]
else:
body = scanner.get_file_at_commit(self._reporoot, filename, sha)
body = self._scanner.get_file_at_commit(filename, sha)
content = yaml.safe_load(body)
for section_name, section_content in content.items():

View File

@ -25,48 +25,6 @@ from reno import utils
LOG = logging.getLogger(__name__)
def _get_current_version(reporoot, branch=None):
"""Return the current version of the repository.
If the repo appears to contain a python project, use setup.py to
get the version so pbr (if used) can do its thing. Otherwise, use
git describe.
"""
cmd = ['git', 'describe', '--tags']
if branch is not None:
cmd.append(branch)
try:
result = utils.check_output(cmd, cwd=reporoot).strip()
if '-' in result:
# Descriptions that come after a commit look like
# 2.0.0-1-abcde, and we want to remove the SHA value from
# the end since we only care about the version number
# itself, but we need to recognize that the change is
# unreleased so keep the -1 part.
result, dash, ignore = result.rpartition('-')
except subprocess.CalledProcessError:
# This probably means there are no tags.
result = '0.0.0'
return result
def get_file_at_commit(reporoot, filename, sha):
"Return the contents of the file if it exists at the commit, or None."
try:
return utils.check_output(
['git', 'show', '%s:%s' % (sha, filename)],
cwd=reporoot,
)
except subprocess.CalledProcessError:
return None
def _file_exists_at_commit(reporoot, filename, sha):
"Return true if the file exists at the given commit."
return bool(get_file_at_commit(reporoot, filename, sha))
def _get_unique_id(filename):
base = os.path.basename(filename)
root, ext = os.path.splitext(base)
@ -78,64 +36,6 @@ def _get_unique_id(filename):
return uniqueid
def _get_branch_base(reporoot, branch):
"Return the tag at base of the branch."
# Based on
# http://stackoverflow.com/questions/1527234/finding-a-branch-point-with-git
# git rev-list $(git rev-list --first-parent \
# ^origin/stable/newton master | tail -n1)^^!
#
# Determine the list of commits accessible from the branch we are
# supposed to be scanning, but not on master.
cmd = [
'git',
'rev-list',
'--first-parent',
branch, # on the branch
'^master', # not on master
]
try:
LOG.debug(' '.join(cmd))
parents = utils.check_output(cmd, cwd=reporoot).strip()
if not parents:
# There are no commits on the branch, yet, so we can use
# our current-version logic.
return _get_current_version(reporoot, branch)
except subprocess.CalledProcessError as e:
LOG.warning('failed to retrieve branch base: %s [%s]',
e, e.output.strip())
return None
parent = parents.splitlines()[-1]
LOG.debug('parent = %r', parent)
# Now get the previous commit, which should be the one we tagged
# to create the branch.
cmd = [
'git',
'rev-list',
'{}^^!'.format(parent),
]
try:
sha = utils.check_output(cmd, cwd=reporoot).strip()
LOG.debug('sha = %r', sha)
except subprocess.CalledProcessError as e:
LOG.warning('failed to retrieve branch base: %s [%s]',
e, e.output.strip())
return None
# Now get the tag for that commit.
cmd = [
'git',
'describe',
'--abbrev=0',
sha,
]
try:
return utils.check_output(cmd, cwd=reporoot).strip()
except subprocess.CalledProcessError as e:
LOG.warning('failed to retrieve branch base: %s [%s]',
e, e.output.strip())
return None
# The git log output from _get_tags_on_branch() looks like this sample
# from the openstack/nova repository for git 1.9.1:
#
@ -207,244 +107,349 @@ def _get_version_tags_on_branch(reporoot, branch):
return tags
def get_notes_by_version(conf):
"""Return an OrderedDict mapping versions to lists of notes files.
class Scanner(object):
The versions are presented in reverse chronological order.
def __init__(self, conf):
self.conf = conf
self.reporoot = self.conf.reporoot
Notes files are associated with the earliest version for which
they were available, regardless of whether they changed later.
def _get_current_version(self, branch=None):
"""Return the current version of the repository.
:param reporoot: Path to the root of the git repository.
:type reporoot: str
"""
If the repo appears to contain a python project, use setup.py to
get the version so pbr (if used) can do its thing. Otherwise, use
git describe.
reporoot = conf.reporoot
notesdir = conf.notespath
branch = conf.branch
earliest_version = conf.earliest_version
collapse_pre_releases = conf.collapse_pre_releases
stop_at_branch_base = conf.stop_at_branch_base
LOG.debug('scanning %s/%s (branch=%s)' % (reporoot, notesdir, branch))
# If the user has not told us where to stop, try to work it out
# for ourselves. If branch is set and is not "master", then we
# want to stop at the base of the branch.
if (stop_at_branch_base and
(not earliest_version) and branch and (branch != 'master')):
LOG.debug('determining earliest_version from branch')
earliest_version = _get_branch_base(reporoot, branch)
if earliest_version and collapse_pre_releases:
if PRE_RELEASE_RE.search(earliest_version):
# The earliest version won't actually be the pre-release
# that might have been tagged when the branch was created,
# but the final version. Strip the pre-release portion of
# the version number.
earliest_version = '.'.join(earliest_version.split('.')[:-1])
LOG.debug('using earliest_version = %r', earliest_version)
# Determine all of the tags known on the branch, in their date
# order. We scan the commit history in topological order to ensure
# we have the commits in the right version, so we might encounter
# the tags in a different order during that phase.
versions_by_date = _get_version_tags_on_branch(reporoot, branch)
LOG.debug('versions by date %r' % (versions_by_date,))
versions = []
earliest_seen = collections.OrderedDict()
# Determine the current version, which might be an unreleased or
# dev version if there are unreleased commits at the head of the
# branch in question. Since the version may not already be known,
# make sure it is in the list of versions by date. And since it is
# the most recent version, go ahead and insert it at the front of
# the list.
current_version = _get_current_version(reporoot, branch)
LOG.debug('current repository version: %s' % current_version)
if current_version not in versions_by_date:
LOG.debug('adding %s to versions by date' % current_version)
versions_by_date.insert(0, current_version)
# Remember the most current filename for each id, to allow for
# renames.
last_name_by_id = {}
# Remember uniqueids that have had files deleted.
uniqueids_deleted = collections.defaultdict(set)
# FIXME(dhellmann): This might need to be more line-oriented for
# longer histories.
log_cmd = [
'git', 'log',
'--topo-order', # force traversal order rather than date order
'--pretty=%x00%H %d', # output contents in parsable format
'--name-only' # only include the names of the files in the patch
]
if branch is not None:
log_cmd.append(branch)
LOG.debug('running %s' % ' '.join(log_cmd))
history_results = utils.check_output(log_cmd, cwd=reporoot)
history = history_results.split('\x00')
current_version = current_version
for h in history:
h = h.strip()
if not h:
continue
# print(h)
hlines = h.splitlines()
# The first line of the block will include the SHA and may
# include tags, the other lines are filenames.
sha = hlines[0].split(' ')[0]
tags = TAG_RE.findall(hlines[0])
# Filter the files based on the notes directory we were
# given. We cannot do this in the git log command directly
# because it means we end up skipping some of the tags if the
# commits being tagged don't include any release note
# files. Even if this list ends up empty, we continue doing
# the other processing so that we record all of the known
# versions.
filenames = []
for f in hlines[2:]:
if fnmatch.fnmatch(f, notesdir + '/*.yaml'):
filenames.append(f)
elif fnmatch.fnmatch(f, notesdir + '/*'):
LOG.warning('found and ignored extra file %s', f)
# If there are no tags in this block, assume the most recently
# seen version.
if not tags:
tags = [current_version]
else:
current_version = tags[0]
LOG.debug('%s has tags %s (%r), updating current version to %s' %
(sha, tags, hlines[0], current_version))
# Remember each version we have seen.
if current_version not in versions:
LOG.debug('%s is a new version' % current_version)
versions.append(current_version)
LOG.debug('%s contains files %s' % (sha, filenames))
# Remember the files seen, using their UUID suffix as a unique id.
for f in filenames:
# Updated as older tags are found, handling edits to release
# notes.
uniqueid = _get_unique_id(f)
LOG.debug('%s: found file %s',
uniqueid, f)
LOG.debug('%s: setting earliest reference to %s' %
(uniqueid, tags[0]))
earliest_seen[uniqueid] = tags[0]
if uniqueid in last_name_by_id:
# We already have a filename for this id from a
# new commit, so use that one in case the name has
# changed.
LOG.debug('%s: was seen before in %s',
uniqueid, last_name_by_id[uniqueid])
continue
elif _file_exists_at_commit(reporoot, f, sha):
LOG.debug('%s: looking for %s in deleted files %s',
uniqueid, f, uniqueids_deleted[uniqueid])
if f in uniqueids_deleted[uniqueid]:
# The file exists in the commit, but was deleted
# later in the history.
LOG.debug('%s: skipping deleted file %s',
uniqueid, f)
else:
# Remember this filename as the most recent version of
# the unique id we have seen, in case the name
# changed from an older commit.
last_name_by_id[uniqueid] = (f, sha)
LOG.debug('%s: remembering %s as filename',
uniqueid, f)
else:
# Track files that have been deleted. The rename logic
# above checks for repeated references to files that
# are deleted later, and the inversion logic below
# checks for any remaining values and skips those
# entries.
LOG.debug('%s: saw a file that no longer exists',
uniqueid)
uniqueids_deleted[uniqueid].add(f)
LOG.debug('%s: deleted files %s',
uniqueid, uniqueids_deleted[uniqueid])
# Invert earliest_seen to make a list of notes files for each
# version.
files_and_tags = collections.OrderedDict()
for v in versions:
files_and_tags[v] = []
# Produce a list of the actual files present in the repository. If
# a note is removed, this step should let us ignore it.
for uniqueid, version in earliest_seen.items():
"""
cmd = ['git', 'describe', '--tags']
if branch is not None:
cmd.append(branch)
try:
base, sha = last_name_by_id[uniqueid]
if base in uniqueids_deleted.get(uniqueid, set()):
LOG.debug('skipping deleted note %s' % uniqueid)
continue
files_and_tags[version].append((base, sha))
except KeyError:
# Unable to find the file again, skip it to avoid breaking
# the build.
msg = ('[reno] unable to find file associated '
'with unique id %r, skipping') % uniqueid
LOG.debug(msg)
print(msg, file=sys.stderr)
result = utils.check_output(cmd, cwd=self.reporoot).strip()
if '-' in result:
# Descriptions that come after a commit look like
# 2.0.0-1-abcde, and we want to remove the SHA value from
# the end since we only care about the version number
# itself, but we need to recognize that the change is
# unreleased so keep the -1 part.
result, dash, ignore = result.rpartition('-')
except subprocess.CalledProcessError:
# This probably means there are no tags.
result = '0.0.0'
return result
# Combine pre-releases into the final release, if we are told to
# and the final release exists.
if collapse_pre_releases:
collapsing = files_and_tags
files_and_tags = collections.OrderedDict()
for ov in versions_by_date:
if ov not in collapsing:
# We don't need to collapse this one because there are
# no notes attached to it.
def _get_branch_base(self, branch):
"Return the tag at base of the branch."
# Based on
# http://stackoverflow.com/questions/1527234/finding-a-branch-point-with-git
# git rev-list $(git rev-list --first-parent \
# ^origin/stable/newton master | tail -n1)^^!
#
# Determine the list of commits accessible from the branch we are
# supposed to be scanning, but not on master.
cmd = [
'git',
'rev-list',
'--first-parent',
branch, # on the branch
'^master', # not on master
]
try:
LOG.debug(' '.join(cmd))
parents = utils.check_output(cmd, cwd=self.reporoot).strip()
if not parents:
# There are no commits on the branch, yet, so we can use
# our current-version logic.
return self._get_current_version(branch)
except subprocess.CalledProcessError as e:
LOG.warning('failed to retrieve branch base: %s [%s]',
e, e.output.strip())
return None
parent = parents.splitlines()[-1]
LOG.debug('parent = %r', parent)
# Now get the previous commit, which should be the one we tagged
# to create the branch.
cmd = [
'git',
'rev-list',
'{}^^!'.format(parent),
]
try:
sha = utils.check_output(cmd, cwd=self.reporoot).strip()
LOG.debug('sha = %r', sha)
except subprocess.CalledProcessError as e:
LOG.warning('failed to retrieve branch base: %s [%s]',
e, e.output.strip())
return None
# Now get the tag for that commit.
cmd = [
'git',
'describe',
'--abbrev=0',
sha,
]
try:
return utils.check_output(cmd, cwd=self.reporoot).strip()
except subprocess.CalledProcessError as e:
LOG.warning('failed to retrieve branch base: %s [%s]',
e, e.output.strip())
return None
def get_file_at_commit(self, filename, sha):
"Return the contents of the file if it exists at the commit, or None."
try:
return utils.check_output(
['git', 'show', '%s:%s' % (sha, filename)],
cwd=self.reporoot,
)
except subprocess.CalledProcessError:
return None
def _file_exists_at_commit(self, filename, sha):
"Return true if the file exists at the given commit."
return bool(self.get_file_at_commit(filename, sha))
def get_notes_by_version(self):
"""Return an OrderedDict mapping versions to lists of notes files.
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
"""
reporoot = self.reporoot
notesdir = self.conf.notespath
branch = self.conf.branch
earliest_version = self.conf.earliest_version
collapse_pre_releases = self.conf.collapse_pre_releases
stop_at_branch_base = self.conf.stop_at_branch_base
LOG.debug('scanning %s/%s (branch=%s)' % (reporoot, notesdir, branch))
# If the user has not told us where to stop, try to work it out
# for ourselves. If branch is set and is not "master", then we
# want to stop at the base of the branch.
if (stop_at_branch_base and
(not earliest_version) and branch and (branch != 'master')):
LOG.debug('determining earliest_version from branch')
earliest_version = self._get_branch_base(branch)
if earliest_version and collapse_pre_releases:
if PRE_RELEASE_RE.search(earliest_version):
# The earliest version won't actually be the pre-release
# that might have been tagged when the branch was created,
# but the final version. Strip the pre-release portion of
# the version number.
earliest_version = '.'.join(
earliest_version.split('.')[:-1]
)
LOG.debug('using earliest_version = %r', earliest_version)
# Determine all of the tags known on the branch, in their date
# order. We scan the commit history in topological order to ensure
# we have the commits in the right version, so we might encounter
# the tags in a different order during that phase.
versions_by_date = _get_version_tags_on_branch(reporoot, branch)
LOG.debug('versions by date %r' % (versions_by_date,))
versions = []
earliest_seen = collections.OrderedDict()
# Determine the current version, which might be an unreleased or
# dev version if there are unreleased commits at the head of the
# branch in question. Since the version may not already be known,
# make sure it is in the list of versions by date. And since it is
# the most recent version, go ahead and insert it at the front of
# the list.
current_version = self._get_current_version(branch)
LOG.debug('current repository version: %s' % current_version)
if current_version not in versions_by_date:
LOG.debug('adding %s to versions by date' % current_version)
versions_by_date.insert(0, current_version)
# Remember the most current filename for each id, to allow for
# renames.
last_name_by_id = {}
# Remember uniqueids that have had files deleted.
uniqueids_deleted = collections.defaultdict(set)
# FIXME(dhellmann): This might need to be more line-oriented for
# longer histories.
log_cmd = [
'git', 'log',
'--topo-order', # force traversal order rather than date order
'--pretty=%x00%H %d', # output contents in parsable format
'--name-only' # only include the names of the files in the patch
]
if branch is not None:
log_cmd.append(branch)
LOG.debug('running %s' % ' '.join(log_cmd))
history_results = utils.check_output(log_cmd, cwd=reporoot)
history = history_results.split('\x00')
current_version = current_version
for h in history:
h = h.strip()
if not h:
continue
pre_release_match = PRE_RELEASE_RE.search(ov)
LOG.debug('checking %r', ov)
if pre_release_match:
# Remove the trailing pre-release part of the version
# from the string.
pre_rel_str = pre_release_match.groups()[0]
canonical_ver = ov[:-len(pre_rel_str)].rstrip('.')
if canonical_ver not in versions_by_date:
# This canonical version was never tagged, so we
# do not want to collapse the pre-releases. Reset
# to the original version.
canonical_ver = ov
else:
LOG.debug('combining into %r', canonical_ver)
# print(h)
hlines = h.splitlines()
# The first line of the block will include the SHA and may
# include tags, the other lines are filenames.
sha = hlines[0].split(' ')[0]
tags = TAG_RE.findall(hlines[0])
# Filter the files based on the notes directory we were
# given. We cannot do this in the git log command directly
# because it means we end up skipping some of the tags if the
# commits being tagged don't include any release note
# files. Even if this list ends up empty, we continue doing
# the other processing so that we record all of the known
# versions.
filenames = []
for f in hlines[2:]:
if fnmatch.fnmatch(f, notesdir + '/*.yaml'):
filenames.append(f)
elif fnmatch.fnmatch(f, notesdir + '/*'):
LOG.warning('found and ignored extra file %s', f)
# If there are no tags in this block, assume the most recently
# seen version.
if not tags:
tags = [current_version]
else:
canonical_ver = ov
if canonical_ver not in files_and_tags:
files_and_tags[canonical_ver] = []
files_and_tags[canonical_ver].extend(collapsing[ov])
current_version = tags[0]
LOG.debug('%s has tags %s (%r), '
'updating current version to %s' %
(sha, tags, hlines[0], current_version))
# Only return the parts of files_and_tags that actually have
# filenames associated with the versions.
trimmed = collections.OrderedDict()
for ov in versions_by_date:
if not files_and_tags.get(ov):
continue
# Sort the notes associated with the version so they are in a
# deterministic order, to avoid having the same data result in
# different output depending on random factors. Earlier
# versions of the scanner assumed the notes were recorded in
# chronological order based on the commit date, but with the
# change to use topological sorting that is no longer
# necessarily true. We want the notes to always show up in the
# same order, but it doesn't really matter what order that is,
# so just sort based on the unique id.
trimmed[ov] = sorted(files_and_tags[ov])
# If we have been told to stop at a version, we can do that
# now.
if earliest_version and ov == earliest_version:
break
# Remember each version we have seen.
if current_version not in versions:
LOG.debug('%s is a new version' % current_version)
versions.append(current_version)
LOG.debug('[reno] found %d versions and %d files',
len(trimmed.keys()), sum(len(ov) for ov in trimmed.values()))
return trimmed
LOG.debug('%s contains files %s' % (sha, filenames))
# Remember the files seen, using their UUID suffix as a unique id.
for f in filenames:
# Updated as older tags are found, handling edits to release
# notes.
uniqueid = _get_unique_id(f)
LOG.debug('%s: found file %s',
uniqueid, f)
LOG.debug('%s: setting earliest reference to %s' %
(uniqueid, tags[0]))
earliest_seen[uniqueid] = tags[0]
if uniqueid in last_name_by_id:
# We already have a filename for this id from a
# new commit, so use that one in case the name has
# changed.
LOG.debug('%s: was seen before in %s',
uniqueid, last_name_by_id[uniqueid])
continue
elif self._file_exists_at_commit(f, sha):
LOG.debug('%s: looking for %s in deleted files %s',
uniqueid, f, uniqueids_deleted[uniqueid])
if f in uniqueids_deleted[uniqueid]:
# The file exists in the commit, but was deleted
# later in the history.
LOG.debug('%s: skipping deleted file %s',
uniqueid, f)
else:
# Remember this filename as the most recent version of
# the unique id we have seen, in case the name
# changed from an older commit.
last_name_by_id[uniqueid] = (f, sha)
LOG.debug('%s: remembering %s as filename',
uniqueid, f)
else:
# Track files that have been deleted. The rename logic
# above checks for repeated references to files that
# are deleted later, and the inversion logic below
# checks for any remaining values and skips those
# entries.
LOG.debug('%s: saw a file that no longer exists',
uniqueid)
uniqueids_deleted[uniqueid].add(f)
LOG.debug('%s: deleted files %s',
uniqueid, uniqueids_deleted[uniqueid])
# Invert earliest_seen to make a list of notes files for each
# version.
files_and_tags = collections.OrderedDict()
for v in versions:
files_and_tags[v] = []
# Produce a list of the actual files present in the repository. If
# a note is removed, this step should let us ignore it.
for uniqueid, version in earliest_seen.items():
try:
base, sha = last_name_by_id[uniqueid]
if base in uniqueids_deleted.get(uniqueid, set()):
LOG.debug('skipping deleted note %s' % uniqueid)
continue
files_and_tags[version].append((base, sha))
except KeyError:
# Unable to find the file again, skip it to avoid breaking
# the build.
msg = ('[reno] unable to find file associated '
'with unique id %r, skipping') % uniqueid
LOG.debug(msg)
print(msg, file=sys.stderr)
# Combine pre-releases into the final release, if we are told to
# and the final release exists.
if collapse_pre_releases:
collapsing = files_and_tags
files_and_tags = collections.OrderedDict()
for ov in versions_by_date:
if ov not in collapsing:
# We don't need to collapse this one because there are
# no notes attached to it.
continue
pre_release_match = PRE_RELEASE_RE.search(ov)
LOG.debug('checking %r', ov)
if pre_release_match:
# Remove the trailing pre-release part of the version
# from the string.
pre_rel_str = pre_release_match.groups()[0]
canonical_ver = ov[:-len(pre_rel_str)].rstrip('.')
if canonical_ver not in versions_by_date:
# This canonical version was never tagged, so we
# do not want to collapse the pre-releases. Reset
# to the original version.
canonical_ver = ov
else:
LOG.debug('combining into %r', canonical_ver)
else:
canonical_ver = ov
if canonical_ver not in files_and_tags:
files_and_tags[canonical_ver] = []
files_and_tags[canonical_ver].extend(collapsing[ov])
# Only return the parts of files_and_tags that actually have
# filenames associated with the versions.
trimmed = collections.OrderedDict()
for ov in versions_by_date:
if not files_and_tags.get(ov):
continue
# Sort the notes associated with the version so they are in a
# deterministic order, to avoid having the same data result in
# different output depending on random factors. Earlier
# versions of the scanner assumed the notes were recorded in
# chronological order based on the commit date, but with the
# change to use topological sorting that is no longer
# necessarily true. We want the notes to always show up in the
# same order, but it doesn't really matter what order that is,
# so just sort based on the unique id.
trimmed[ov] = sorted(files_and_tags[ov])
# If we have been told to stop at a version, we can do that
# now.
if earliest_version and ov == earliest_version:
break
LOG.debug('[reno] found %d versions and %d files',
len(trimmed.keys()), sum(len(ov) for ov in trimmed.values()))
return trimmed

View File

@ -45,19 +45,19 @@ class TestCache(base.TestCase):
""")
}
def _get_note_body(self, reporoot, filename, sha):
def _get_note_body(self, filename, sha):
return self.note_bodies.get(filename, '')
def setUp(self):
super(TestCache, self).setUp()
self.useFixture(
mockpatch.Patch('reno.scanner.get_file_at_commit',
mockpatch.Patch('reno.scanner.Scanner.get_file_at_commit',
new=self._get_note_body)
)
self.c = config.Config('.')
def test_build_cache_db(self):
with mock.patch('reno.scanner.get_notes_by_version') as gnbv:
with mock.patch('reno.scanner.Scanner.get_notes_by_version') as gnbv:
gnbv.return_value = self.scanner_output
db = cache.build_cache_db(
self.c,

View File

@ -197,6 +197,7 @@ class Base(base.TestCase):
self.temp_dir = self.useFixture(fixtures.TempDir()).path
self.reporoot = os.path.join(self.temp_dir, 'reporoot')
self.c = config.Config(self.reporoot)
self.scanner = scanner.Scanner(self.c)
self._git_setup()
self._counter = itertools.count(1)
self.get_note_num = lambda: next(self._counter)
@ -206,7 +207,7 @@ class BasicTest(Base):
def test_non_python_no_tags(self):
filename = self._add_notes_file()
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -219,7 +220,7 @@ class BasicTest(Base):
def test_python_no_tags(self):
self._make_python_package()
filename = self._add_notes_file()
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -233,7 +234,7 @@ class BasicTest(Base):
filename = self._add_notes_file()
self._add_other_file('not-a-release-note.txt')
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -246,7 +247,7 @@ class BasicTest(Base):
def test_note_commit_tagged(self):
filename = self._add_notes_file()
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -260,7 +261,7 @@ class BasicTest(Base):
self._make_python_package()
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0')
filename = self._add_notes_file()
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -275,7 +276,7 @@ class BasicTest(Base):
self._add_other_file('ignore-1.txt')
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0')
self._add_other_file('ignore-2.txt')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -290,7 +291,7 @@ class BasicTest(Base):
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0')
f1 = self._add_notes_file()
f2 = self._add_notes_file()
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -305,7 +306,7 @@ class BasicTest(Base):
f1 = self._add_notes_file(commit=False)
f2 = self._add_notes_file()
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -321,7 +322,7 @@ class BasicTest(Base):
f1 = self._add_notes_file()
self._run_git('tag', '-s', '-m', 'first tag', '2.0.0')
f2 = self._add_notes_file()
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -341,7 +342,7 @@ class BasicTest(Base):
f2 = f1.replace('slug1', 'slug2')
self._run_git('mv', f1, f2)
self._git_commit('rename note file')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -360,7 +361,7 @@ class BasicTest(Base):
f2 = f1.replace('slug1', 'slug0')
self._run_git('mv', f1, f2)
self._git_commit('rename note file')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -379,7 +380,7 @@ class BasicTest(Base):
with open(os.path.join(self.reporoot, f1), 'w') as f:
f.write('---\npreamble: new contents for file')
self._git_commit('edit note file')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -398,7 +399,7 @@ class BasicTest(Base):
f2 = f1.replace('slug1', 'slug2')
self._run_git('mv', f1, f2)
self._git_commit('rename note file')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -420,7 +421,7 @@ class BasicTest(Base):
'slug1-0000000000000001')
self._run_git('mv', f1, f2)
self._git_commit('rename note file')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -442,7 +443,7 @@ class BasicTest(Base):
self.c.override(
earliest_version='2.0.0',
)
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -462,7 +463,7 @@ class BasicTest(Base):
self._run_git('rm', f1)
self._git_commit('remove note file')
self._run_git('tag', '-s', '-m', 'first tag', '2.0.0')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -492,7 +493,7 @@ class BasicTest(Base):
'--pretty=%H %d',
'--name-only')
self.addDetail('git log', text_content(log_results))
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -511,7 +512,7 @@ class PreReleaseTest(Base):
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0.0a1')
f1 = self._add_notes_file('slug1')
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0.0a2')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -527,7 +528,7 @@ class PreReleaseTest(Base):
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0.0b1')
f1 = self._add_notes_file('slug1')
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0.0b2')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -543,7 +544,7 @@ class PreReleaseTest(Base):
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0.0rc1')
f1 = self._add_notes_file('slug1')
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0.0rc2')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -568,7 +569,7 @@ class PreReleaseTest(Base):
self.c.override(
collapse_pre_releases=True,
)
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -590,7 +591,7 @@ class PreReleaseTest(Base):
self.c.override(
collapse_pre_releases=True,
)
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -615,7 +616,7 @@ class PreReleaseTest(Base):
self.c.override(
collapse_pre_releases=True,
)
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -643,7 +644,7 @@ class MergeCommitTest(Base):
self._run_git('merge', '--no-ff', 'test_merge_commit')
self._add_other_file('ignore-2.txt')
self._run_git('tag', '-s', '-m', 'second tag', '2.0.0')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -671,7 +672,7 @@ class MergeCommitTest(Base):
self._run_git('merge', '--no-ff', 'test_merge_commit')
self._add_other_file('ignore-2.txt')
self._run_git('tag', '-s', '-m', 'second tag', '2.0.0')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -703,7 +704,7 @@ class MergeCommitTest(Base):
self._add_other_file('ignore-2.txt')
self._run_git('tag', '-s', '-m', 'third tag', '2.0.0')
self._add_other_file('ignore-3.txt')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -739,7 +740,7 @@ class MergeCommitTest(Base):
self._add_other_file('ignore-2.txt')
self._run_git('tag', '-s', '-m', 'third tag', '2.0.0')
self._add_other_file('ignore-3.txt')
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -789,7 +790,7 @@ class BranchTest(Base):
f21 = self._add_notes_file('slug21')
log_text = self._run_git('log')
self.addDetail('git log', text_content(log_text))
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -814,7 +815,7 @@ class BranchTest(Base):
self.c.override(
branch='stable/2',
)
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -841,7 +842,7 @@ class BranchTest(Base):
self.c.override(
stop_at_branch_base=False,
)
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -877,7 +878,7 @@ class BranchTest(Base):
branch='stable/4',
collapse_pre_releases=False,
)
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -913,7 +914,7 @@ class BranchTest(Base):
branch='stable/4',
collapse_pre_releases=True,
)
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -946,7 +947,7 @@ class BranchTest(Base):
self.c.override(
branch='stable/4',
)
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -979,7 +980,7 @@ class BranchTest(Base):
self.c.override(
branch='stable/4',
)
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -1010,7 +1011,7 @@ class BranchTest(Base):
self.c.override(
branch='stable/4',
)
raw_results = scanner.get_notes_by_version(self.c)
raw_results = self.scanner.get_notes_by_version()
results = {
k: [f for (f, n) in v]
for (k, v) in raw_results.items()
@ -1183,3 +1184,33 @@ class GetTagsParseTest(base.TestCase):
actual = scanner._get_version_tags_on_branch('reporoot',
branch=None)
self.assertEqual(self.EXPECTED, actual)
class VersionTest(Base):
def setUp(self):
super(VersionTest, self).setUp()
self._make_python_package()
self.f1 = self._add_notes_file('slug1')
self._run_git('tag', '-s', '-m', 'first tag', '1.0.0')
self.f2 = self._add_notes_file('slug2')
self._run_git('tag', '-s', '-m', 'first tag', '2.0.0')
self._add_notes_file('slug3')
self._run_git('tag', '-s', '-m', 'first tag', '3.0.0')
def test_tagged_head(self):
self.scanner = scanner.Scanner(self.c)
results = self.scanner._get_current_version(None)
self.assertEqual(
'3.0.0',
results,
)
def test_head_after_tag(self):
self._add_notes_file('slug4')
self.scanner = scanner.Scanner(self.c)
results = self.scanner._get_current_version(None)
self.assertEqual(
'3.0.0-1',
results,
)