From 7b2d52408c157c82027e2728dc9b2dcbdcc5a674 Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Fri, 1 Mar 2024 14:58:39 +0100 Subject: [PATCH] [FIX] oca-towncrier: Add support for Mardown format If readme fragments uses the md format, generate the history file into the same format --- tests/test_towncrier.py | 42 ++++++++++++++++++++++-- tools/oca_towncrier.py | 46 +++++++++++++++++++------- tools/towncrier-template.md | 65 +++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 14 deletions(-) create mode 100644 tools/towncrier-template.md diff --git a/tests/test_towncrier.py b/tests/test_towncrier.py index b0511d40..90334d68 100644 --- a/tests/test_towncrier.py +++ b/tests/test_towncrier.py @@ -10,13 +10,17 @@ def test_make_issue_format(): assert ( - _make_issue_format("OCA", "repo") + _make_issue_format("OCA", "repo", "rst") == "`#{issue} `_" ) + assert ( + _make_issue_format("OCA", "repo", "md") + == "[#{issue}](https://github.com/OCA/repo/issues/{issue})" + ) def test_prepare_config(tmp_path): - with _prepare_config(str(tmp_path), "OCA", "repo") as fn: + with _prepare_config(str(tmp_path), "OCA", "repo") as (fn, result_file): with open(fn) as f: pyproject = toml.load(f) assert set(pyproject["tool"]["towncrier"].keys()) == { @@ -60,3 +64,37 @@ def test_oca_towncrier(tmp_path): - Bugfix description. (`#50 `_) """ ) + + +def test_oca_towncrier_md(tmp_path): + addon_path = tmp_path / "addon_a" + readme_path = addon_path / "readme" + history_path = readme_path / "HISTORY.md" + news_path = readme_path / "newsfragments" + news_path.mkdir(parents=True) + (news_path / "50.bugfix").write_text("Bugfix description.") + (readme_path / "description.md").write_text("Some description") + runner = CliRunner() + runner.invoke( + oca_towncrier, + [ + "--addon-dir", + str(addon_path), + "--version", + "14.0.1.0.1", + "--date", + "2021-12-31", + "--repo", + "therepo", + ], + ) + assert history_path.exists() + assert history_path.read_text() == textwrap.dedent( + """\ + ## 14.0.1.0.1 (2021-12-31) + + #### Bugfixes + + - Bugfix description. ([#50](https://github.com/OCA/therepo/issues/50)) + """ + ) diff --git a/tools/oca_towncrier.py b/tools/oca_towncrier.py index 08c4ff96..691e7722 100644 --- a/tools/oca_towncrier.py +++ b/tools/oca_towncrier.py @@ -16,34 +16,56 @@ from .manifest import read_manifest -def _make_issue_format(org, repo): - return "`#{{issue}} `_".format( - org=org, repo=repo +def _make_issue_format(org, repo, fragment_format): + if fragment_format == "md": + return f"[#{{issue}}](https://github.com/{org}/{repo}/issues/{{issue}})" + return f"`#{{issue}} `_" + + +def _get_towncrier_template(fragment_format): + return os.path.join( + os.path.dirname(__file__), f"towncrier-template.{fragment_format}" ) -def _get_towncrier_template(): - return os.path.join(os.path.dirname(__file__), "towncrier-template.rst") +def _get_readme_fragment_format(addon_dir): + """Detect the format of the readme fragment to generate (md or rst)""" + fragment_format = "rst" + readme_dir = os.path.join(addon_dir, "readme") + if not os.path.isdir(readme_dir): + return fragment_format + files = os.listdir(readme_dir) + files = [f for f in files if not f.startswith(".")] + for ext in ("md", "rst"): + # list files in the readme directory and check the extension of the first file + if files and os.path.isfile(os.path.join(addon_dir, "readme", files[0])): + if files[0].endswith(f".{ext}"): + fragment_format = ext + break + return fragment_format @contextlib.contextmanager def _prepare_config(addon_dir, org, repo): """Inject towncrier options in pyproject.toml""" + # first detect expected format (we support both md and rst) + fragment_format = _get_readme_fragment_format(addon_dir) with tempfile.NamedTemporaryFile(dir=addon_dir, mode="w") as config_file: + result_file = os.path.join("readme", f"HISTORY.{fragment_format}") config = { "tool": { "towncrier": { - "template": _get_towncrier_template(), - "underlines": ["~"], - "issue_format": _make_issue_format(org, repo), + "template": _get_towncrier_template(fragment_format), + "underlines": ["~" if fragment_format == "rst" else ""], + "issue_format": _make_issue_format(org, repo, fragment_format), "directory": "readme/newsfragments", - "filename": "readme/HISTORY.rst", + "filename": result_file, } } } toml.dump(config, config_file) config_file.flush() - yield config_file.name + yield config_file.name, result_file @click.command( @@ -82,7 +104,7 @@ def oca_towncrier(addon_dirs, version, date, org, repo, commit): if not any(not f.startswith(".") for f in os.listdir(news_dir)): continue addon_version = version or read_manifest(addon_dir)["version"] - with _prepare_config(addon_dir, org, repo) as config_file_name: + with _prepare_config(addon_dir, org, repo) as (config_file_name, result_file): subprocess.call( [ sys.executable, @@ -99,7 +121,7 @@ def oca_towncrier(addon_dirs, version, date, org, repo, commit): cwd=addon_dir, ) paths.append(news_dir) - paths.append(os.path.join(addon_dir, "readme", "HISTORY.rst")) + paths.append(os.path.join(addon_dir, result_file)) if commit: commit_if_needed(paths, message="[UPD] changelog", add=False) diff --git a/tools/towncrier-template.md b/tools/towncrier-template.md new file mode 100644 index 00000000..41773757 --- /dev/null +++ b/tools/towncrier-template.md @@ -0,0 +1,65 @@ +{% if render_title %} +{% if versiondata.name %} +## {{ versiondata.name }} {{ versiondata.version }} ({{ versiondata.date }}) +{% else %} +## {{ versiondata.version }} ({{ versiondata.date }}) +{% endif %} +{% endif %} +{% for section, _ in sections.items() %} +{% if section %} + +### {{section}} +{% endif %} + +{% if sections[section] %} +{% for category, val in definitions.items() if category in sections[section] %} +#### {{ definitions[category]['name'] }} + +{% if definitions[category]['showcontent'] %} +{% for text, values in sections[section][category].items() %} +- {{ text }} +{%- if values %} +{% if "\n - " in text or '\n * ' in text %} + + + ( +{%- else %} + ( +{%- endif -%} +{%- for issue in values %} +{{ issue.split(": ", 1)[0] }}{% if not loop.last %}, {% endif %} +{%- endfor %} +) +{% else %} + +{% endif %} +{% endfor %} + +{% else %} +- {% for issue in sections[section][category][''] %} +{{ issue.split(": ", 1)[0] }}{% if not loop.last %}, {% endif %} +{% endfor %} + + +{% endif %} +{% if issues_by_category[section][category] and "]: " in issues_by_category[section][category][0] %} +{% for issue in issues_by_category[section][category] %} +{{ issue }} +{% endfor %} + +{% endif %} +{% if sections[section][category]|length == 0 %} +No significant changes. + +{% else %} +{% endif %} +{% endfor %} +{% else %} +No significant changes. + +{% endif %} +{% endfor +%} +{# +This comment adds one more newline at the end of the rendered newsfile content. +In this way the there are 2 newlines between the latest release and the previous release content. +#}