hass-release/hassrelease/changelog.py

178 lines
5.1 KiB
Python

from collections import OrderedDict
from datetime import datetime
from distutils.version import StrictVersion
INFO_TEMPLATE = "([@{0}] - [#{1}])"
PR_TEMPLATE = "([#{0}])"
DOC_TEMPLATE = "([{0} docs])"
LINK_DEF_USER = "[@{0}]: {1}"
LINK_DEF_PR = "[#{0}]: {1}"
GITHUB_LINK_DEF_DOC = "[{0} docs]: https://www.home-assistant.io/integrations/{0}/"
LINK_DEF_DOC = "[{0} docs]: /integrations/{0}/"
IGNORE_LINE_LABELS = set(["reverted"])
LABEL_HEADERS = {
"new-integration": "New Integrations",
"new-platform": "New Platforms",
"breaking-change": "Breaking Changes",
"cherry-picked": "Beta Fixes",
}
# Handle special cases. None values will be ignored.
WEBSITE_DIVIDER = """## If you need help...
...don't hesitate to use our very active [forums](https://community.home-assistant.io/) or join us for a little [chat](https://discord.gg/c5DvZ4e).
## Reporting Issues
Experiencing issues introduced by this release? Please report them in our [issue tracker](https://github.com/home-assistant/home-assistant/issues). Make sure to fill in all fields of the issue template.
<!--more-->
"""
def automation_link(platform, website_tags):
"""Return automation doc link."""
if platform == "automation.homeassistant":
val = "home-assistant"
elif platform == "automation.numeric_state":
val = "numeric-state"
else:
val = platform[len("automation.") :]
if website_tags:
format = "[{} docs]: /docs/automation/trigger/#{}-trigger"
else:
format = "[{} docs]: https://www.home-assistant.io/docs/automation/trigger/#{}-trigger"
return format.format(platform, val)
LABEL_MAP = {
"discovery.": None,
"recorder.": None,
"automation.": automation_link,
"emulated_hue.": None,
"homeassistant.": None,
"cloud.": lambda pl, _wt: f"[{pl} docs]: https://www.nabucasa.com/config/",
}
def _process_doc_label(label, parts, links, website_tags):
"""Process doc labels."""
item = None
if label.startswith("integration: "):
item = label[len("integration: ") :]
if not item:
return
part = DOC_TEMPLATE.format(item)
if website_tags:
link = LINK_DEF_DOC.format(item)
else:
link = GITHUB_LINK_DEF_DOC.format(item)
for match, action in LABEL_MAP.items():
if item.startswith(match):
if action is None:
# Ignore item completely
return
else:
link = action(item, website_tags)
break
parts.append(part)
links.add(link)
def generate(release, prs, *, website_tags):
"""Generate a changelog.
website_tags: boolean if we should include tags for home-assistant.io
"""
label_groups = OrderedDict()
label_groups["new-integration"] = []
label_groups["new-platform"] = []
label_groups["breaking-change"] = []
if release.version.version[-1] == 0:
# Only add 'beta fix' for 0-release
label_groups["cherry-picked"] = []
changes = []
links = set()
for line in release.log_lines():
parts = ["-", line.message]
# Filter out git commits that are not merge commits
if line.pr is None:
continue
pr = prs.get(line.pr)
if (
pr.milestone is not None
and StrictVersion(pr.milestone.title).version != release.version.version
): # Ignore beta version tag
continue
labels = [label.name for label in pr.labels()]
# Filter out commits for which the PR has one of the ignored labels
if any(label in IGNORE_LINE_LABELS for label in labels):
continue
user = pr.user
links.add(LINK_DEF_USER.format(user.login, user.html_url))
parts.append(INFO_TEMPLATE.format(user.login, pr.number))
links.add(LINK_DEF_PR.format(pr.number, pr.html_url))
for label in labels:
_process_doc_label(label, parts, links, website_tags)
for label in labels:
if label in label_groups:
if label == "cherry-picked":
parts.append("(beta fix)")
else:
parts.append("({})".format(label))
msg = " ".join(parts)
changes.append(msg)
for label in labels:
if label not in label_groups:
continue
label_groups[label].append(msg)
outp = []
if release.is_patch_release:
if website_tags:
now = datetime.now()
outp.append(
f'## Release {release.version} - {now.strftime("%B")} {now.day}'
)
outp.append("")
else:
for label, prs in label_groups.items():
if label == "breaking-change" and website_tags:
outp.append(WEBSITE_DIVIDER)
if not prs:
continue
outp.append(f"## {LABEL_HEADERS[label]}")
outp.append("")
outp.extend(prs)
outp.append("")
outp.append("## All changes")
outp.append("")
outp.extend(changes)
outp.append("")
outp.extend(sorted(links))
return "\n".join(outp)