mirror of https://github.com/mamba-org/mamba.git
208 lines
6.7 KiB
Python
208 lines
6.7 KiB
Python
# Script to release any of the mamba packages.
|
|
# This script has no cli parameters and only read info from the root changelog
|
|
# which must have been modified by executing `update_changelog.py`.
|
|
# Please refer to `update_changelog.py` for more info about the release process.
|
|
|
|
import copy
|
|
import datetime
|
|
import re
|
|
from version_scheme import version_info
|
|
|
|
template = {"version": None, "changes": []}
|
|
|
|
templates = {
|
|
"libmamba": "libmamba/include/mamba/version.hpp.tmpl",
|
|
"micromamba": "micromamba/src/version.hpp.tmpl",
|
|
"libmambapy": "libmambapy/src/libmambapy/version.py.tmpl",
|
|
}
|
|
|
|
|
|
def apply_changelog(name, version, changes):
|
|
def template_substitute(contents):
|
|
x = contents.replace("{{ version_major }}", version.major)
|
|
x = x.replace("{{ version_minor }}", version.minor)
|
|
x = x.replace("{{ version_patch }}", version.patch)
|
|
x = x.replace("{{ version_is_prerelease }}", "1" if version.pre_release else "0")
|
|
x = x.replace("{{ version_prerelease_name }}", version.pre_release)
|
|
x = x.replace("{{ version_name }}", version.name)
|
|
return x
|
|
|
|
if name in templates:
|
|
template = templates[name]
|
|
with open(template) as fi:
|
|
final = template_substitute(fi.read())
|
|
with open(template[: -len(".tmpl")], "w") as fo:
|
|
fo.write(final)
|
|
|
|
# version has been processed, we can now produce the changes
|
|
res = ""
|
|
today = datetime.date.today()
|
|
fmt_today = today.strftime("%B %d, %Y")
|
|
|
|
res += f"## {name} {version} ({fmt_today})\n\n"
|
|
|
|
for idx, c in enumerate(changes):
|
|
if c.startswith("-"):
|
|
if idx > 0 and not changes[idx - 1].startswith("- "):
|
|
res += f"\n{c}\n"
|
|
else:
|
|
res += f"{c}\n"
|
|
else:
|
|
res += f"{c}\n"
|
|
res += "\n"
|
|
|
|
cl_file = name + "/CHANGELOG.md"
|
|
with open(cl_file) as fi:
|
|
prev_cl = fi.read()
|
|
with open(cl_file, "w") as fo:
|
|
fo.write(res + prev_cl)
|
|
|
|
|
|
def commands(release_version, changes):
|
|
commit_msg = ", ".join([f"{x} {changes[x]['version']}" for x in changes])
|
|
|
|
files_to_commit = ""
|
|
for c in changes:
|
|
files_to_commit += f" {c}/CHANGELOG.md \\\n"
|
|
files_to_commit += f" {templates[c][: -len('.tmpl')]} \\\n"
|
|
files_to_commit = files_to_commit[:-3]
|
|
|
|
for c in changes:
|
|
files_to_commit += f" {templates[c][: -len('.tmpl')]} \\\n"
|
|
print("\n\n--- REVERT ---\n\n")
|
|
print(f"git checkout origin/main -- \\\n{files_to_commit[:-3]}\n\n")
|
|
|
|
print("\n\n--- COMMIT ---\n\n")
|
|
print("pre-commit run --all")
|
|
print("git diff")
|
|
files_to_commit += " CHANGELOG.md \\\n"
|
|
print(f"git commit -m 'release {commit_msg}' \\\n{files_to_commit[:-3]}")
|
|
|
|
print(f"git tag {release_version}")
|
|
|
|
|
|
class Section:
|
|
def __init__(self):
|
|
self.items = []
|
|
self.applies_to = ["all"]
|
|
self.text = ""
|
|
|
|
|
|
class Item:
|
|
def __init__(self):
|
|
self.applies_to = ["all"]
|
|
self.text = ""
|
|
|
|
|
|
def populate_changes(name, sections, changes):
|
|
el = changes[name]
|
|
|
|
def applies(x):
|
|
return "all" in x or name in x
|
|
|
|
for s in sections:
|
|
s_applies = applies(s.applies_to)
|
|
if s_applies and len(s.items):
|
|
s_applies = any(applies(i.applies_to) for i in s.items)
|
|
|
|
if s_applies:
|
|
if s != sections[0]:
|
|
el["changes"].append("\n" + s.text.strip())
|
|
else:
|
|
el["changes"].append(s.text.strip())
|
|
for i in s.items:
|
|
if applies(i.applies_to):
|
|
el["changes"].append(f"- {i.text.strip()}")
|
|
|
|
|
|
MARKDOWN_H2 = "## "
|
|
|
|
|
|
def main():
|
|
changes = {}
|
|
with open("CHANGELOG.md") as fi:
|
|
contents = fi.readlines()
|
|
|
|
for idx, line in enumerate(contents):
|
|
if line.startswith(MARKDOWN_H2):
|
|
release_start = idx + 1
|
|
break
|
|
|
|
brackets_re = re.compile(r"\[(.*?)\]")
|
|
|
|
# section with groups, heading + items
|
|
sections = []
|
|
in_section = False
|
|
|
|
release_version = None
|
|
contents = contents[release_start:]
|
|
for idx, line in enumerate(contents):
|
|
if line.startswith("Release"):
|
|
release_re = re.compile(r"Release*:\s+(\d\.\d\.\d[\.\w]*)\s+\(([\w,\s]+)\)\s*")
|
|
if matches := re.search(release_re, line):
|
|
if release_version is not None:
|
|
raise ValueError(
|
|
"multiple release lines (starting with 'Release: ...') found in changelog for last change - consider re-running `update_changelog.py`"
|
|
)
|
|
release_version = matches.group(1)
|
|
projects = matches.group(2).replace(",", " ").split()
|
|
print(f"projects: {projects}")
|
|
for project in projects:
|
|
# because `micromamba` is now the name of the `mamba` project's directory, we ignore it
|
|
if project != "mamba":
|
|
changes[project] = copy.deepcopy(template)
|
|
changes[project]["version"] = release_version
|
|
continue
|
|
|
|
if contents[idx + 1].startswith(MARKDOWN_H2):
|
|
break
|
|
|
|
if line.strip() == "" or line[0] == "-":
|
|
in_section = False
|
|
|
|
if line.strip() == "":
|
|
continue
|
|
|
|
if line[0] != "-":
|
|
if not in_section:
|
|
sections.append(Section())
|
|
in_section = True
|
|
sections[-1].text += line
|
|
|
|
if m := re.search(brackets_re, line):
|
|
if in_section:
|
|
sections[-1].applies_to = [x.strip() for x in m.groups(1)[0].split(",")]
|
|
else:
|
|
sections[-1].items.append(Item())
|
|
sections[-1].items[-1].text = line[m.end() :].strip()
|
|
sections[-1].items[-1].applies_to = [x.strip() for x in m.groups(1)[0].split(",")]
|
|
|
|
else:
|
|
if line.startswith(" "):
|
|
if in_section:
|
|
sections[-1].text += " " + line.strip()
|
|
else:
|
|
sections[-1].items[-1].text += line.strip()
|
|
else:
|
|
if not in_section:
|
|
sections[-1].items.append(Item())
|
|
sections[-1].items[-1].text = line.strip()
|
|
sections[-1].items[-1].applies_to = ["all"]
|
|
|
|
if release_version is None:
|
|
raise ValueError("Version to release not found - use `update_changelog.py` to specify it")
|
|
|
|
release_version = version_info(release_version)
|
|
|
|
for project_name in changes:
|
|
populate_changes(project_name, sections, changes)
|
|
|
|
for project_name in changes:
|
|
apply_changelog(project_name, release_version, changes[project_name]["changes"])
|
|
|
|
commands(release_version, changes)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|