Skip to content

Commit

Permalink
feat(reana-dev): add git-aggregate-changelog (#789)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdonadoni committed Mar 13, 2024
1 parent a4cb84c commit 6210b11
Showing 1 changed file with 254 additions and 0 deletions.
254 changes: 254 additions & 0 deletions reana/reana_dev/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@

import datetime
import os
import re
import subprocess
import sys
from typing import Optional

import click
import yaml

from reana.config import (
COMPONENTS_USING_SHARED_MODULE_COMMONS,
Expand All @@ -28,6 +30,8 @@
PYTHON_VERSION_FILE,
RELEASE_COMMIT_REGEX,
REPO_LIST_ALL,
REPO_LIST_CLUSTER_INFRASTRUCTURE,
REPO_LIST_CLUSTER_RUNTIME_BATCH,
REPO_LIST_PYTHON_REQUIREMENTS,
REPO_LIST_SHARED,
)
Expand Down Expand Up @@ -1586,4 +1590,254 @@ def git_tag(component, exclude_components): # noqa: D301
run_command(f"git tag {current_version}", component=component)


def get_previous_versions(components, override={}):
"""Get the version of each component at the time of the previous REANA release."""
helm_values = yaml.safe_load(

Check warning on line 1595 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1595

Added line #L1595 was not covered by tests
run_command(
"git show HEAD~1:helm/reana/values.yaml",
component="reana",
return_output=True,
)
)

prev_versions = dict(override)
prev_server = helm_values["components"]["reana_server"]["image"].split(":")[1]
for component in components:
if component in override:
continue

Check warning on line 1607 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1603-L1607

Added lines #L1603 - L1607 were not covered by tests

if (

Check warning on line 1609 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1609

Added line #L1609 was not covered by tests
component
in REPO_LIST_CLUSTER_INFRASTRUCTURE + REPO_LIST_CLUSTER_RUNTIME_BATCH
):
# cluster components: get version from docker image in
# Helm values of previous REANA release
image = helm_values["components"][component.replace("-", "_")]["image"]
prev_version = image.split(":")[1]
elif component in REPO_LIST_SHARED:

Check warning on line 1617 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1615-L1617

Added lines #L1615 - L1617 were not covered by tests
# shared components: read version from requirements.txt of reana-server
# of previous REANA release
requirement = run_command(

Check warning on line 1620 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1620

Added line #L1620 was not covered by tests
f"git show {prev_server}:requirements.txt | grep '^{component}'",
component="reana-server",
return_output=True,
)
prev_version = re.search("==([a-zA-Z0-9.-_]+)", requirement).group(1)
elif component == "reana":

Check warning on line 1626 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1625-L1626

Added lines #L1625 - L1626 were not covered by tests
# reana helm chart: get version from manifest of previous commit
chart_manifest = yaml.safe_load(

Check warning on line 1628 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1628

Added line #L1628 was not covered by tests
run_command(
"git show HEAD~1:helm/reana/Chart.yaml",
component="reana",
return_output=True,
)
)
prev_version = chart_manifest["version"]

Check warning on line 1635 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1635

Added line #L1635 was not covered by tests
else:
raise ValueError(f"Not able to find previous version of {component}")
prev_versions[component] = prev_version

Check warning on line 1638 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1637-L1638

Added lines #L1637 - L1638 were not covered by tests

return prev_versions

Check warning on line 1640 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1640

Added line #L1640 was not covered by tests


def get_formatted_changelog_lines(component, versions):
"""Read and format the changelog lines of given component and versions.
The changelog will be reformatted so that:
- commit types do not create subsections (e.g. `### Build`)
- the commit type is prepended to each commit message
- all sections are moved one level down in the hierarchy
Example:
```
## [0.9.8](https://github.com/reanahub/reana-commons/compare/0.9.7...0.9.8) (2024-03-01)
### Build
* **python:** change extra names to comply with PEP 685 [...]
```
becomes
```
#### reana-commons [0.9.8](https://github.com/reanahub/reana-commons/compare/0.9.7...0.9.8) (2024-03-01)
* [Build] **python:** change extra names to comply with PEP 685 [...]
```
"""
changelog_path = os.path.join(get_srcdir(component), "CHANGELOG.md")
with open(changelog_path) as f:
changelog_lines = f.readlines()

Check warning on line 1671 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1669-L1671

Added lines #L1669 - L1671 were not covered by tests

formatted_lines = []
is_version_to_add = False
current_section = ""
for line in changelog_lines:

Check warning on line 1676 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1673-L1676

Added lines #L1673 - L1676 were not covered by tests
# check if release in header is part of releases we are interested in
matches = re.match(r"##\s+\[?([\d.]+)", line)
if matches:
is_version_to_add = matches.group(1) in versions
if not is_version_to_add:
continue

Check warning on line 1682 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1678-L1682

Added lines #L1678 - L1682 were not covered by tests

if line.startswith("### "):

Check warning on line 1684 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1684

Added line #L1684 was not covered by tests
# commit type (e.g. fix, feat, ...)
current_section = line[len("### ") :].strip()
elif line.startswith("## "):

Check warning on line 1687 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1686-L1687

Added lines #L1686 - L1687 were not covered by tests
# release header
line = f"#### {component}" + line[len("##") :]
if formatted_lines:

Check warning on line 1690 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1689-L1690

Added lines #L1689 - L1690 were not covered by tests
# add empty line before previous release changelog
formatted_lines.append("\n")
formatted_lines.append(line)
formatted_lines.append("\n")
elif line.startswith("*"):

Check warning on line 1695 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1692-L1695

Added lines #L1692 - L1695 were not covered by tests
# release please format, bullet points with '*'
formatted_lines.append(f"* [{current_section}]" + line[1:])
elif line.startswith("-"):

Check warning on line 1698 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1697-L1698

Added lines #L1697 - L1698 were not covered by tests
# old changelog format, bullet points with '-'
formatted_lines.append("*" + line[1:])

Check warning on line 1700 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1700

Added line #L1700 was not covered by tests

return formatted_lines

Check warning on line 1702 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1702

Added line #L1702 was not covered by tests


def substitute_version_changelog(component, version, new_lines):
"""Substitute the changelog of the provided version."""
changelog_path = os.path.join(get_srcdir(component), "CHANGELOG.md")
with open(changelog_path) as changelog_file:
changelog_lines = changelog_file.readlines()

Check warning on line 1709 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1707-L1709

Added lines #L1707 - L1709 were not covered by tests

# find the idx of the given release
idx_begin = None
for i, line in enumerate(changelog_lines):
if line.startswith("## ") and version in line:
idx_begin = i
break

Check warning on line 1716 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1712-L1716

Added lines #L1712 - L1716 were not covered by tests

if idx_begin is None:
raise ValueError(f"Could not find changelog of {component} {version}")

Check warning on line 1719 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1718-L1719

Added lines #L1718 - L1719 were not covered by tests

idx_end = idx_begin + 1
while idx_end < len(changelog_lines):
if changelog_lines[idx_end].startswith("## "):
break
idx_end += 1

Check warning on line 1725 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1721-L1725

Added lines #L1721 - L1725 were not covered by tests

new_changelog = (

Check warning on line 1727 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1727

Added line #L1727 was not covered by tests
changelog_lines[: idx_begin + 2] # let's keep header and blank line
+ new_lines
+ changelog_lines[idx_end:]
)

with open(changelog_path, "w") as changelog_file:
changelog_file.writelines(new_changelog)

Check warning on line 1734 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1733-L1734

Added lines #L1733 - L1734 were not covered by tests


def append_after_version_changelog(component, version, new_lines):
"""Append the given lines after the changelog of the provided version."""
changelog_path = os.path.join(get_srcdir(component), "CHANGELOG.md")
with open(changelog_path) as changelog_file:
changelog_lines = changelog_file.readlines()

Check warning on line 1741 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1739-L1741

Added lines #L1739 - L1741 were not covered by tests

# find the idx of the release that follows the given one
idx_insert = None
found_given_version = False
for i, line in enumerate(changelog_lines):
if line.startswith("## "):
if version in line:
found_given_version = True
elif found_given_version:
idx_insert = i
break

Check warning on line 1752 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1744-L1752

Added lines #L1744 - L1752 were not covered by tests

if idx_insert is None:
raise ValueError(f"Could not find changelog of {component} {version}")

Check warning on line 1755 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1754-L1755

Added lines #L1754 - L1755 were not covered by tests

new_changelog = (

Check warning on line 1757 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1757

Added line #L1757 was not covered by tests
changelog_lines[:idx_insert] + new_lines + changelog_lines[idx_insert:]
)

with open(changelog_path, "w") as changelog_file:
changelog_file.writelines(new_changelog)

Check warning on line 1762 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1761-L1762

Added lines #L1761 - L1762 were not covered by tests


@git_commands.command(name="git-aggregate-changelog")
@click.option(
"--previous-reana-client",
help="Which is the version of reana-client that was released "
"for the last REANA release?",
required=True,
)
def get_aggregate_changelog(previous_reana_client): # noqa: D301
"""Aggregate the changelog of all REANA components.
Aggregate the changelog of all REANA components and append it to the main changelog of REANA.
This is useful for creating the changelog of a new REANA release.
All the repositories of the cluster components, shared components, `reana-client` and
`reana` must be checked out at the respective release commits.
:param previous_reana_client: The version of reana-client that was part of the previous REANA release.
:type previous_reana_client: str
"""
# all the components whose changelogs will be aggregated
changelog_components = ["reana"] + sorted(

Check warning on line 1785 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1785

Added line #L1785 was not covered by tests
["reana-client"]
+ REPO_LIST_SHARED
+ REPO_LIST_CLUSTER_INFRASTRUCTURE
+ REPO_LIST_CLUSTER_RUNTIME_BATCH
)
for component in changelog_components:
if not is_last_commit_release_commit(component):
click.secho(

Check warning on line 1793 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1791-L1793

Added lines #L1791 - L1793 were not covered by tests
f"The last commit of {component} is not a release commit. "
"Please make sure you have the release commit checked out.",
fg="red",
)
sys.exit(1)

Check warning on line 1798 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1798

Added line #L1798 was not covered by tests

# get all the versions of the components as they were when the previous REANA version was released
prev_versions = get_previous_versions(

Check warning on line 1801 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1801

Added line #L1801 was not covered by tests
changelog_components, {"reana-client": previous_reana_client}
)

aggregated_changelog_lines = []
for component in changelog_components:
prev_version = prev_versions[component]

Check warning on line 1807 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1805-L1807

Added lines #L1805 - L1807 were not covered by tests

# get all tags reachable from latest release but not part of previous REANA release
versions_to_add = set(

Check warning on line 1810 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1810

Added line #L1810 was not covered by tests
run_command(
f"git tag --no-merged {prev_version} --merged",
component,
return_output=True,
).splitlines()
)

# also add current version, as it might not be tagged yet
versions_to_add.add(get_current_component_version_from_source_files(component))

Check warning on line 1819 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1819

Added line #L1819 was not covered by tests

aggregated_changelog_lines += get_formatted_changelog_lines(

Check warning on line 1821 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1821

Added line #L1821 was not covered by tests
component, versions_to_add
)
aggregated_changelog_lines += ["\n"]

Check warning on line 1824 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1824

Added line #L1824 was not covered by tests

current_reana_version = get_current_component_version_from_source_files("reana")

Check warning on line 1826 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1826

Added line #L1826 was not covered by tests

# add headers
aggregated_changelog_lines = [

Check warning on line 1829 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1829

Added line #L1829 was not covered by tests
f"### :sparkles: What's new in REANA {current_reana_version}\n",
"\n",
"TODO: copy here the blog post introduction + link to blog post\n",
"\n",
f"### :zap: Detailed changelog for REANA {current_reana_version} components\n",
"\n",
] + aggregated_changelog_lines

substitute_version_changelog(

Check warning on line 1838 in reana/reana_dev/git.py

View check run for this annotation

Codecov / codecov/patch

reana/reana_dev/git.py#L1838

Added line #L1838 was not covered by tests
"reana", current_reana_version, aggregated_changelog_lines
)


git_commands_list = list(git_commands.commands.values())

0 comments on commit 6210b11

Please sign in to comment.