Files
deb-python-reno/reno/scanner.py
Doug Hellmann d1aaff7bc7 reverse slug and uuid order in filenames
Change-Id: I7346f6bd9965db3680143c0f5bdd0448d12e05eb
2015-10-01 21:50:08 +00:00

164 lines
5.6 KiB
Python

# 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 collections
import os.path
import re
import subprocess
from reno import utils
_TAG_PAT = re.compile('tag: ([\d\.]+)')
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)
uniqueid = root[-16:]
if '-' in uniqueid:
# This is an older file with the UUID at the beginning
# of the name.
uniqueid = root[:16]
return uniqueid
# TODO(dhellmann): Add branch arg?
def get_notes_by_version(reporoot, notesdir, branch=None):
"""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.
"""
versions = []
earliest_seen = collections.OrderedDict()
# Determine the current version, which might be an unreleased or dev
# version.
current_version = _get_current_version(reporoot, branch)
# print('current_version = %s' % current_version)
# Remember the most current filename for each id, to allow for
# renames.
last_name_by_id = {}
# FIXME(dhellmann): This might need to be more line-oriented for
# longer histories.
log_cmd = ['git', 'log', '--pretty=%x00%H %d', '--name-only']
if branch is not None:
log_cmd.append(branch)
log_cmd.extend(['--', notesdir])
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_PAT.findall(hlines[0])
filenames = hlines[2:]
# 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]
# Remember each version we have seen.
if current_version not in versions:
versions.append(current_version)
# 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)
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.
continue
if _file_exists_at_commit(reporoot, f, sha):
# 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)
# 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():
base, sha = last_name_by_id[uniqueid]
files_and_tags[version].append((base, sha))
for version, filenames in files_and_tags.items():
files_and_tags[version] = list(reversed(filenames))
return files_and_tags