|
|
|
@ -935,92 +935,80 @@ class MatrixUnits(Units):
|
|
|
|
|
changelog_lines = []
|
|
|
|
|
if target_version == 'unstable':
|
|
|
|
|
# generate towncrier log
|
|
|
|
|
tc_path = os.path.join(CHANGELOG_DIR, api_name)
|
|
|
|
|
if os.path.isdir(tc_path):
|
|
|
|
|
logger.info("Generating towncrier changelog for: %s" % api_name)
|
|
|
|
|
p = subprocess.Popen(
|
|
|
|
|
['towncrier', '--version', 'unstable', '--name', api_name, '--draft'],
|
|
|
|
|
cwd=tc_path,
|
|
|
|
|
stderr=subprocess.PIPE,
|
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
|
)
|
|
|
|
|
stdout, stderr = p.communicate()
|
|
|
|
|
if p.returncode != 0:
|
|
|
|
|
# Something broke - dump as much information as we can
|
|
|
|
|
logger.error("Towncrier exited with code %s" % p.returncode)
|
|
|
|
|
logger.error(stdout.decode('UTF-8'))
|
|
|
|
|
logger.error(stderr.decode('UTF-8'))
|
|
|
|
|
raw_log = ""
|
|
|
|
|
else:
|
|
|
|
|
raw_log = stdout.decode('UTF-8')
|
|
|
|
|
|
|
|
|
|
# This is a bit of a hack, but it does mean that the log at least gets *something*
|
|
|
|
|
# to tell us it broke
|
|
|
|
|
if not raw_log.startswith("unstable"):
|
|
|
|
|
logger.error("Towncrier appears to have failed to generate a changelog")
|
|
|
|
|
logger.error(raw_log)
|
|
|
|
|
raw_log = ""
|
|
|
|
|
changelog_lines = raw_log.splitlines()
|
|
|
|
|
changelog_lines = self._read_towncrier_changelog(api_name)
|
|
|
|
|
else:
|
|
|
|
|
# read in the existing RST changelog
|
|
|
|
|
logger.info("Reading changelog RST for %s" % api_name)
|
|
|
|
|
rst_path = os.path.join(CHANGELOG_DIR, "%s.rst" % api_name)
|
|
|
|
|
with open(rst_path, 'r', encoding="utf-8") as f:
|
|
|
|
|
changelog_lines = f.readlines()
|
|
|
|
|
changelog_lines = self._read_rst_changelog(api_name)
|
|
|
|
|
|
|
|
|
|
# Parse the changelog lines to find the header we're looking for and therefore
|
|
|
|
|
# the changelog body.
|
|
|
|
|
prev_line = None
|
|
|
|
|
title_part = None
|
|
|
|
|
changelog_body_lines = []
|
|
|
|
|
have_changelog = False
|
|
|
|
|
for line in changelog_lines:
|
|
|
|
|
if prev_line is None:
|
|
|
|
|
prev_line = line
|
|
|
|
|
continue
|
|
|
|
|
if not title_part:
|
|
|
|
|
# Titles we care about are underlined with at least 3 equal signs
|
|
|
|
|
if re.match("^[=]{3,}$", line.strip()):
|
|
|
|
|
logger.info("Found header %s" % prev_line)
|
|
|
|
|
title_part = prev_line.strip()
|
|
|
|
|
continue
|
|
|
|
|
prev_line = line
|
|
|
|
|
else:
|
|
|
|
|
# we have a title, start parsing the body
|
|
|
|
|
if re.match("^[=]{3,}$", line.strip()):
|
|
|
|
|
# we hit another title. prev_line will be the new section's header.
|
|
|
|
|
# do a check to see if the section we just read is the one we want - if
|
|
|
|
|
# it is, use that changelog and move on. If it isn't, keep reading.
|
|
|
|
|
if title_part == target_version:
|
|
|
|
|
changelogs[api_name] = "".join(changelog_body_lines)
|
|
|
|
|
have_changelog = True
|
|
|
|
|
break
|
|
|
|
|
# not the section we want - start the next section
|
|
|
|
|
title_part = changelog_body_lines.pop().strip()
|
|
|
|
|
changelog_body_lines = []
|
|
|
|
|
continue
|
|
|
|
|
if re.match("^[-]{3,}$", line.strip()):
|
|
|
|
|
# the last line is a subheading - drop this line because it's the underline
|
|
|
|
|
# and that causes problems with rendering. We'll keep the header text though.
|
|
|
|
|
continue
|
|
|
|
|
if line.strip().startswith(".. "):
|
|
|
|
|
# skip comments
|
|
|
|
|
continue
|
|
|
|
|
if re.match("^[=]{3,}$", line.strip()):
|
|
|
|
|
# the last line was a header - use that as our new title_part
|
|
|
|
|
title_part = prev_line.strip()
|
|
|
|
|
continue
|
|
|
|
|
if re.match("^[-]{3,}$", line.strip()):
|
|
|
|
|
# the last line is a subheading - drop this line because it's the underline
|
|
|
|
|
# and that causes problems with rendering. We'll keep the header text though.
|
|
|
|
|
continue
|
|
|
|
|
if line.strip().startswith(".. "):
|
|
|
|
|
# skip comments
|
|
|
|
|
continue
|
|
|
|
|
if title_part == target_version:
|
|
|
|
|
# if we made it this far, append the line to the changelog body. We indent it so
|
|
|
|
|
# that it renders correctly in the section. We also add newlines so that there's
|
|
|
|
|
# intentionally blank lines that make rst2html happy.
|
|
|
|
|
changelog_body_lines.append(" " + line + '\n')
|
|
|
|
|
# do some quick checks to see if the last read section is our changelog
|
|
|
|
|
if not have_changelog:
|
|
|
|
|
logger.info("No changelog - testing %s == %s" % (target_version, title_part,))
|
|
|
|
|
if title_part == target_version and len(changelog_body_lines) > 0:
|
|
|
|
|
changelogs[api_name] = "".join(changelog_body_lines)
|
|
|
|
|
else:
|
|
|
|
|
raise ValueError("No changelog for %s at %s" % (api_name, target_version,))
|
|
|
|
|
|
|
|
|
|
if len(changelog_body_lines) > 0:
|
|
|
|
|
changelogs[api_name] = "".join(changelog_body_lines)
|
|
|
|
|
else:
|
|
|
|
|
raise ValueError("No changelog for %s at %s" % (api_name, target_version,))
|
|
|
|
|
|
|
|
|
|
# return our `dict[api_name] => changelog` as the last step.
|
|
|
|
|
return changelogs
|
|
|
|
|
|
|
|
|
|
def _read_towncrier_changelog(self, api_name):
|
|
|
|
|
tc_path = os.path.join(CHANGELOG_DIR, api_name)
|
|
|
|
|
if os.path.isdir(tc_path):
|
|
|
|
|
logger.info("Generating towncrier changelog for: %s" % api_name)
|
|
|
|
|
p = subprocess.Popen(
|
|
|
|
|
['towncrier', '--version', 'unstable', '--name', api_name, '--draft'],
|
|
|
|
|
cwd=tc_path,
|
|
|
|
|
stderr=subprocess.PIPE,
|
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
|
)
|
|
|
|
|
stdout, stderr = p.communicate()
|
|
|
|
|
if p.returncode != 0:
|
|
|
|
|
# Something broke - dump as much information as we can
|
|
|
|
|
logger.error("Towncrier exited with code %s" % p.returncode)
|
|
|
|
|
logger.error(stdout.decode('UTF-8'))
|
|
|
|
|
logger.error(stderr.decode('UTF-8'))
|
|
|
|
|
raw_log = ""
|
|
|
|
|
else:
|
|
|
|
|
raw_log = stdout.decode('UTF-8')
|
|
|
|
|
|
|
|
|
|
# This is a bit of a hack, but it does mean that the log at least gets *something*
|
|
|
|
|
# to tell us it broke
|
|
|
|
|
if not raw_log.startswith("unstable"):
|
|
|
|
|
logger.error("Towncrier appears to have failed to generate a changelog")
|
|
|
|
|
logger.error(raw_log)
|
|
|
|
|
raw_log = ""
|
|
|
|
|
return raw_log.splitlines()
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
def _read_rst_changelog(self, api_name):
|
|
|
|
|
logger.info("Reading changelog RST for %s" % api_name)
|
|
|
|
|
rst_path = os.path.join(CHANGELOG_DIR, "%s.rst" % api_name)
|
|
|
|
|
with open(rst_path, 'r', encoding="utf-8") as f:
|
|
|
|
|
return f.readlines()
|
|
|
|
|
|
|
|
|
|
def load_unstable_warnings(self, substitutions):
|
|
|
|
|
warning = """
|
|
|
|
|
.. WARNING::
|
|
|
|
|