"""Core update-version workflow implementation for release command."""
import csv
import re
from datetime import UTC, datetime
from pathlib import Path
from ..helpers.logger import logger
MAGIC_DATE = "1970-01-01"
RELEASE_ENTRY_TEMPLATE = """\
Added
=====
Changed
=======
Deprecated
==========
Removed
=======
Fixed
=====
"""
def date_get_today() -> str:
"""Returns the date string for the output"""
return datetime.now(tz=UTC).strftime("%Y-%m-%d")
def get_previous_release(root: Path) -> str:
"""Return the previous version number"""
path = root / "docs/general/releases.csv"
with open(path, encoding="utf-8") as f:
reader = csv.DictReader(f, delimiter=";")
for row in reader:
if row["foxBMS 2"] != "vx.y.z":
return row["foxBMS 2"][1:]
return ""
def update_c_h_files(root: Path, iso_date_today: str, _from: str, _to: str) -> None:
"""Update .c, and .h files"""
all_c_h_files = (
list((root / "conf").rglob("**/*.[c|h]"))
+ list((root / "docs").rglob("**/*.[c|h]"))
+ list((root / "src").rglob("**/*.[c|h]"))
+ list((root / "tests").rglob("**/*.[c|h]"))
+ list((root / "tools/crc").rglob("**/*.[c|h]"))
+ [
Path(
"tests/cli/pre_commit_scripts/test_check_sections/unknown-file-extension.abc"
)
]
)
updated = re.compile(r" \* @updated (\d{4}-\d{2}-\d{2}) \(date of last update\)")
updated_new = f" * @updated {iso_date_today} (date of last update)"
old_version = f" * @version v{_from}"
new_version = f" * @version v{_to}"
logger.debug("from: %s", old_version)
logger.debug("to: %s", new_version)
for i in all_c_h_files:
do_replace = False
try:
txt = i.read_text(encoding="utf-8")
except (FileNotFoundError, OSError, UnicodeDecodeError):
continue
if (
" * @author foxBMS Team" in txt
or "ignore-author-comment" in i.name
or "no-author-comment" in i.name
or "nxp_mc33775a-ll" in i.name
):
do_replace = True
if not do_replace:
continue
txt = i.read_text(encoding="ascii")
logger.debug("Found foxBMS 2 file: %s", i)
if old_version in txt:
logger.debug("...Replacing version")
txt = txt.replace(old_version, new_version, 1)
logger.debug("...Replacing updated")
txt = updated.sub(updated_new, txt)
i.write_text(txt, encoding="ascii")
def update_wscript(root: Path, _from: str, _to: str) -> None:
"""Update wscript"""
wscript = root / "wscript"
txt = wscript.read_text(encoding="utf-8")
txt = txt.replace(f'VERSION = "{_from}"', f'VERSION = "{_to}"')
wscript.write_text(txt, encoding="utf-8")
def update_citation(root: Path, iso_date_today: str, _from: str, _to: str) -> None:
"""Update citation file"""
citation = root / "CITATION.cff"
txt = citation.read_text(encoding="utf-8")
txt = txt.replace(f'version: "{_from}"', f'version: "{_to}"')
if _to == "x.y.z":
txt = re.sub(r"date-released: .*", f'date-released: "{MAGIC_DATE}"', txt)
else:
txt = txt.replace(
f'date-released: "{MAGIC_DATE}"', f'date-released: "{iso_date_today}"'
)
citation.write_text(txt, encoding="utf-8")
def update_changelog(root: Path, iso_date_today: str, _from: str, _to: str) -> None:
"""Update changelog"""
changelog = root / "docs/general/changelog.rst"
logger.debug("Patching %s", changelog)
txt = changelog.read_text(encoding="utf-8")
if _to == "x.y.z":
tmp = txt.splitlines()
tmp.insert(
24,
f"********************\n[x.y.z] - {MAGIC_DATE}\n********************\n\n"
".. include:: ./changelog-entries/vx.y.z.txt\n",
)
txt = "\n".join(tmp) + "\n"
else:
txt = txt.replace(f"[{_from}] - {MAGIC_DATE}", f"[{_to}] - {iso_date_today}")
txt = txt.replace(
".. include:: ./changelog-entries/vx.y.z.txt",
f".. include:: ./changelog-entries/v{_to}.txt",
)
changelog_entry = root / "docs/general/changelog-entries/vx.y.z.txt"
changelog_entry.rename(root / f"docs/general/changelog-entries/v{_to}.txt")
changelog_entry = root / "docs/general/changelog-entries/vx.y.z.txt"
changelog_entry.write_text(RELEASE_ENTRY_TEMPLATE, encoding="utf-8")
changelog.write_text(txt, encoding="utf-8")
def update_commit_fragments(
root: Path, previous_release: str, _from: str, _to: str
) -> None:
"""Update the next-release commit fragment and create the one for the release"""
if _to == "x.y.z":
change_type = "<Major/Minor/Bugfix>"
else:
try:
_to_l = [int(i) for i in _to.split(".")]
previous_release_l = [int(i) for i in previous_release.split(".")]
change_type = "Bugfix"
if _to_l[1] > previous_release_l[1]:
change_type = "Minor"
if _to_l[0] > previous_release_l[0]:
change_type = "Major"
except ValueError as exc:
err_msg = "unexpected version identifier"
raise SystemExit(err_msg) from exc
commit_msg_file = root / "docs/general/commit-msgs/next-release.txt"
logger.debug("Patching %s", commit_msg_file)
txt = commit_msg_file.read_text(encoding="utf-8")
txt = txt.replace("<Major/Minor/Bugfix>", change_type)
txt = txt.replace(f"foxBMS 2 (v{_from})", f"foxBMS 2 (v{_to})")
txt = txt.replace(
f"v{_from}/general/changelog.html",
f"v{_to}/general/changelog.html",
)
if _to != "x.y.z":
(commit_msg_file.parent / f"release-v{_to}.txt").write_text(
txt, encoding="utf-8"
)
txt = """<Major/Minor/Bugfix> release of foxBMS 2 (vx.y.z)
* <add text here>
For a detailed list of changes, please refer to the documentation at
https://iisb-foxbms.iisb.fraunhofer.de/foxbms/gen2/docs/html/vx.y.z/general/changelog.html.
"""
commit_msg_file.write_text(txt, encoding="utf-8")
def update_release_csv(root: Path, iso_date_today: str, _from: str, _to: str) -> None:
"""Update release.csv"""
releases = root / "docs/general/releases.csv"
txt = releases.read_text(encoding="utf-8")
if _to == "x.y.z":
tmp = txt.splitlines()
if tmp[1].startswith(f"v{_to}"):
err_msg = f"Something went wrong. {releases} already sets 'v{_to}'."
raise SystemExit(err_msg)
updated_txt = (
[tmp[0]]
+ [
f"v{_to}; {MAGIC_DATE}; https://iisb-foxbms.iisb.fraunhofer.de/foxbms/gen2/docs/html/v{_to}/"
]
+ tmp[1:]
)
txt = "\n".join(updated_txt) + "\n"
else:
txt = txt.replace(f"v{_from};", f"v{_to};", 1)
txt = txt.replace(f"{MAGIC_DATE};", f"{iso_date_today};", 1)
txt = txt.replace(f"/v{_from}/", f"/v{_to}/", 1)
releases.write_text(txt, encoding="utf-8")
def apply_update_version(root: Path, _from: str, _to: str) -> None:
"""Apply all low-level version update steps to release-managed files."""
previous_release = get_previous_release(root)
if not previous_release:
err_msg = "Could not determine previous version."
raise SystemExit(err_msg)
iso_date_today = date_get_today()
update_c_h_files(root, iso_date_today, _from, _to)
update_wscript(root, _from, _to)
update_citation(root, iso_date_today, _from, _to)
update_changelog(root, iso_date_today, _from, _to)
update_commit_fragments(root, previous_release, _from, _to)
update_release_csv(root, iso_date_today, _from, _to)