Skip to content

Commit

Permalink
chore: polished release note
Browse files Browse the repository at this point in the history
Rate limit · GitHub

Access has been restricted

You have triggered a rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

dovahcrow committed Jul 28, 2020
1 parent c1bdb3e commit 7b9a235
Showing 4 changed files with 262 additions and 3 deletions.
21 changes: 19 additions & 2 deletions Justfile
Original file line number Diff line number Diff line change
@@ -74,6 +74,19 @@ release version:
esac
done

echo ================ Release Note ================
poetry run python scripts/release-note.py $(git rev-parse develop)
echo ================ Release Note ================
echo
echo Does release note looks good?

select yn in "Yes" "No"; do
case $yn in
Yes ) break;;
No ) exit;;
esac
done

# Begin of the real stuff!

# Create new release branch
@@ -102,10 +115,14 @@ release version:
poetry build

echo "Creating release draft"
semantic-release changelog | sed "1iv${to_version}\n" | hub release create -d -a "dist/dataprep-${to_version}-py3-none-any.whl" -a "dist/dataprep-${to_version}.tar.gz" -F - "v${to_version}"


poetry run python scripts/release-note.py $(git rev-parse release/v${to_version}^) | sed "1iv${to_version}\n" | hub release create -d -a "dist/dataprep-${to_version}-py3-none-any.whl" -a "dist/dataprep-${to_version}.tar.gz" -F - "v${to_version}"


@ensure-git-clean:
if [ ! -z "$(git status --porcelain)" ]; then echo "Git tree is not clean, commit first"; exit 1; fi

@release-note hash="":
echo ================ Release Note ================
poetry run python scripts/release-note.py {{hash}}
echo ================ Release Note ================
44 changes: 43 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -72,6 +72,7 @@ sphinx-autodoc-typehints = "^1.10.3"
ipython = "^7.13.0"
rope = "^0.16.0"
seaborn = "^0.10.1"
gitpython = "^3.1.7"

[tool.black]
line-length = 88
199 changes: 199 additions & 0 deletions scripts/release-note.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
"""ReleaseNote
Usage:
release-note [<hash>]
"""

import re
from binascii import hexlify
from collections import defaultdict
from typing import Dict, List, Optional, Tuple

from docopt import docopt
from git import Commit, Repo
from jinja2 import Template

VERSION_REG = re.compile(r"^v(\d+)\.(\d+)\.(\d+)")
MESSAGE_REG = re.compile(
r"^(?P<type>feat|perf|docs|fix|test|chore)\((?P<scope>.+?)\)?: ?(?P<message>.+)"
)

RELEASE_NOTE_TEMPLATE = Template(
"""
{% if fixes %}
## Bugfixes 🐛
{% for fix in fixes -%}
* {{fix.scope}}: {{fix.message}} ({{fix.hash}})
{% endfor %}{% endif %}
{% if feats %}
## Features ✨
{% for feat in feats -%}
* {{feat.scope}}: {{feat.message}} ({{feat.hash}})
{% endfor %}
{% endif %}
{% if tests %}
## Code Quality + Testing 💯
{% for test in tests -%}
* {{test.scope}}: {{test.message}} ({{test.hash}})
{% endfor %}
{% endif %}
{% if perfs %}
## Performance 🚀
{% for perf in perfs -%}
* {{perf.scope}}: {{perf.message}} ({{perf.hash}})
{% endfor %}
{% endif %}
{% if docs %}
## Documentation 📃
{% for doc in docs -%}
* {{doc.scope}}: {{doc.message}} ({{doc.hash}})
{% endfor %}
{% endif %}
{% if chores %}
## Others 🛠
{% for chore in chores -%}
* {{chore.scope}}: {{chore.message}} ({{chore.hash}})
{% endfor %}
{% endif %}
## Contributors this release 🏆
The following users contributed code to DataPrep since the last release.
{% for author in authors -%}
* {{author.name}} \<{{author.email}}\> {% if author.first %}(First time contributor) ⭐️{% endif %}
{% endfor %}
🎉🎉 Thank you! 🎉🎉
"""
)


def main() -> None:
args = docopt(__doc__)

repo = Repo(".")
assert repo.bare == False

(develop,) = [branch for branch in repo.branches if branch.name == "develop"]
seed = develop.commit

hash = args["<hash>"]
if hash is not None:
seed = find_commit_by_hash(seed, hash)
if seed is None:
raise ValueError(f"Cannot find commit with hash {hash}")

this_commits, handle = commits_since_previous(seed)
else:
if VERSION_REG.findall(seed.message):
this_commits, handle = commits_since_previous(*seed.parents)
else:
this_commits, handle = commits_since_previous(seed)

version = VERSION_REG.match(handle.message).group()

previous_commits: List[Commit] = []
while handle is not None:
commits, handle = commits_since_previous(*handle.parents)
previous_commits.extend(commits.values())

this_authors = {commit.author for commit in this_commits.values()}

first_time_authors = this_authors - {commit.author for commit in previous_commits}

authors = [
{
"name": author.name,
"email": author.email,
"first": author in first_time_authors,
}
for author in this_authors
]

authors = sorted(authors, key=lambda a: a["name"].upper())

notes = defaultdict(list)
for commit in this_commits.values():
match = MESSAGE_REG.match(commit.message)
if match is not None:
notes[match.group("type")].append(
{
"scope": match.group("scope"),
"message": match.group("message"),
"hash": hexlify(commit.binsha).decode()[:8],
}
)

note = RELEASE_NOTE_TEMPLATE.render(
feats=notes["feat"],
perfs=notes["perf"],
docs=notes["docs"],
chore=notes["chore"],
tests=notes["test"],
fixes=notes["fix"],
authors=authors,
)

new_note = note.replace("\n\n\n", "\n\n")
while new_note != note:
note = new_note
new_note = note.replace("\n\n\n", "\n\n")
note = new_note

note = note.strip()

print(note)


def commits_since_previous(
*seed_commits: Commit,
) -> Tuple[Dict[str, Commit], Optional[Commit]]:
stack = list(seed_commits)
commits = {}

previous = None
while stack:
commit = stack.pop()

if commit.binsha in commits:
continue

matches = VERSION_REG.findall(commit.message)

if matches:
previous = commit
continue

commits[commit.binsha] = commit
stack.extend(commit.parents)

return commits, previous


def find_commit_by_hash(seed: Commit, hash: str) -> Optional[Commit]:
stack = [seed]
commits = {}

while stack:
commit = stack.pop()

if commit.binsha in commits:
continue
hex = hexlify(commit.binsha)
if hex.startswith(hash.encode()):
return commit

commits[commit.binsha] = commit
stack.extend(commit.parents)

return None


if __name__ == "__main__":
main()

0 comments on commit 7b9a235

Please sign in to comment.