Skip to content

chore(CI): Add release notes check #6682

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/hierarchy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ jobs:
uses: "./.github/actions/diffs"
id: diff

release-notes-check:
concurrency:
group: release-notes-check-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/develop' }}
if: github.event_name == 'pull_request'
runs-on: [ubuntu-latest]
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- run: |
cd scripts/release_notes
./run.sh check-pr ${{ github.event.pull_request.number }}

dprint-format:
runs-on: [self-hosted-x64]
concurrency:
Expand Down
63 changes: 50 additions & 13 deletions scripts/release_notes/release_notes.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ def parse_args():
help="The commit to check, defaults to HEAD.",
)

check_p = sub_parser.add_parser(
"check-pr",
description=(
"Check if the release notes section of a given commit is complete, "
"i.e. that every impacted component has a non-empty note."
),
)

check_p.add_argument(
"pr_number",
help="The number of the PR to check.",
)

return vars(parser.parse_args())


Expand All @@ -134,32 +147,47 @@ def extract_notes_from_rebase_commit(commit):
pr_number = data[0]["number"]
pr_notes = data[0]["body"] if data[0]["body"] else ""
return pr_number, pr_notes

def extract_notes_from_pr(pr_number):
url = f"https://api.github.com/repos/iotaledger/iota/pulls/{pr_number}"
headers = {
"Accept": "application/vnd.github.v3+json",
}
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req) as response:
data = json.load(response)
pr_notes = data["body"] if data["body"] else ""
return pr_notes


def extract_notes(commit, seen):
"""Get release notes from a commit message.
def extract_notes(commit_or_pr, seen, is_pr):
"""Get release notes from a commit message or a PR description.

Find the 'Release notes' section in the commit message, and
extract the notes for each impacted area (area that has been
Finds the 'Release notes' section in the message, and
extracts the notes for each impacted area (area that has been
ticked).

Returns a tuple of the PR number and a dictionary of impacted
areas mapped to their release note. Each release note indicates
whether it has a note and whether it was checked (ticked).

"""
message = git("show", "-s", "--format=%B", commit)
if is_pr:
pr = commit_or_pr
message = extract_notes_from_pr(pr)
else:
message = git("show", "-s", "--format=%B", commit_or_pr)

# Extract PR number from squashed commits
match = RE_PR.match(message)
pr = match.group(1) if match else None
# Extract PR number from squashed commits
match = RE_PR.match(message)
pr = match.group(1) if match else None

result = {}

notes = ""
if pr is None:
# Extract PR number from rebase commits if it's not a squashed commit
pr, notes = extract_notes_from_rebase_commit(commit)
pr, notes = extract_notes_from_rebase_commit(commit_or_pr)
else:
# Otherwise, find the release notes section from the squashed commit message
match = RE_HEADING.search(message)
Expand Down Expand Up @@ -223,7 +251,7 @@ def print_changelog(pr, log):
print(log)


def do_check(commit):
def do_check(commit_or_pr, is_pr):
"""Check if the release notes section of a given commit is complete.

This means that every impacted component has a non-empty note,
Expand All @@ -232,9 +260,13 @@ def do_check(commit):

"""

_, notes = extract_notes(commit, set())
_, notes = extract_notes(commit_or_pr, set(), is_pr)

issues = []
any_checked = False
for impacted, note in notes:
any_checked |= note.checked;

if impacted not in NOTE_ORDER:
issues.append(f" - Found unfamiliar impact area '{impacted}'.")

Expand All @@ -246,10 +278,13 @@ def do_check(commit):
f" - '{impacted}' has a release note but is not checked: {note.note}"
)

if not any_checked and len(notes) > 0:
issues.append(f" - No checked items in release notes");

if not issues:
return

print(f"Found issues with release notes in {commit}:")
print(f"Found issues with release notes in {commit_or_pr}:")
for issue in issues:
print(issue)
sys.exit(1)
Expand Down Expand Up @@ -323,4 +358,6 @@ def do_generate(from_, to):
if args["command"] == "generate":
do_generate(args["from"], args["to"])
elif args["command"] == "check":
do_check(args["commit"])
do_check(args["commit"], False)
elif args["command"] == "check-pr":
do_check(args["pr_number"], True)
Loading