From e89ce31fe9cf381246e2c171e48959822e8cf3f9 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Tue, 3 Sep 2024 16:19:10 +0100 Subject: [PATCH] Add command to produce CHANGELOG.md file (#239) --- .github/workflows/tox.yml | 1 + .gitignore | 1 + .readthedocs.yml | 3 ++ .tool-versions | 1 + pyproject.toml | 2 +- samples/integration/ansible-lint.txt | 1 + samples/integration/cookiecutter.txt | 1 + samples/integration/flask-babel.txt | 1 + samples/integration/nox.txt | 1 + samples/integration/podman.txt | 1 + samples/integration/typeshed.txt | 1 + src/mk/tools/pre.py | 79 ++++++++++++++++++++++------ tox.ini | 7 ++- 13 files changed, 82 insertions(+), 18 deletions(-) create mode 100644 .tool-versions diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 6a15e04..cfc42e6 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -14,6 +14,7 @@ concurrency: cancel-in-progress: true env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} FORCE_COLOR: 1 # tox, pytest, ansible-lint PY_COLORS: 1 diff --git a/.gitignore b/.gitignore index c22da3f..07e6f2d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ src/mk/_version.py .coverage* coverage.xml CMakeFiles +CHANGELOG.md diff --git a/.readthedocs.yml b/.readthedocs.yml index cdf030c..92eb51f 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -10,6 +10,9 @@ build: tools: python: "3.11" commands: + - asdf --version + - asdf plugin add github-cli + - asdf install - pip install --user tox - python3 -m tox -e docs -- --strict --site-dir=_readthedocs/html/ python: diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..98f8ebc --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +github-cli 2.55.0 diff --git a/pyproject.toml b/pyproject.toml index be0c018..0b86faf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ source = ["src", ".tox/*/site-packages"] exclude_also = ["pragma: no cover", "if TYPE_CHECKING:"] omit = ["test/*"] # Increase it just so it would pass on any single-python run -fail_under = 47 +fail_under = 46 skip_covered = true skip_empty = true # During development we might remove code (files) with coverage data, and we dont want to fail: diff --git a/samples/integration/ansible-lint.txt b/samples/integration/ansible-lint.txt index 7fefd82..c87833c 100644 --- a/samples/integration/ansible-lint.txt +++ b/samples/integration/ansible-lint.txt @@ -1,5 +1,6 @@ alerts build +changelog docs drafts eco diff --git a/samples/integration/cookiecutter.txt b/samples/integration/cookiecutter.txt index dcf8794..7ccd34c 100644 --- a/samples/integration/cookiecutter.txt +++ b/samples/integration/cookiecutter.txt @@ -1,5 +1,6 @@ alerts build +changelog clean-build clean-coverage clean-docs-build diff --git a/samples/integration/flask-babel.txt b/samples/integration/flask-babel.txt index 6b30137..a6bc24f 100644 --- a/samples/integration/flask-babel.txt +++ b/samples/integration/flask-babel.txt @@ -1,5 +1,6 @@ alerts build +changelog drafts install prs diff --git a/samples/integration/nox.txt b/samples/integration/nox.txt index 7a2f23b..8cb0d4a 100644 --- a/samples/integration/nox.txt +++ b/samples/integration/nox.txt @@ -1,5 +1,6 @@ alerts build +changelog conda_tests cover docs diff --git a/samples/integration/podman.txt b/samples/integration/podman.txt index cb82cfa..7ef3e14 100644 --- a/samples/integration/podman.txt +++ b/samples/integration/podman.txt @@ -2,6 +2,7 @@ alerts binaries binaries2 binaries23 +changelog clean clean-binaries docs diff --git a/samples/integration/typeshed.txt b/samples/integration/typeshed.txt index bfea9b4..a4367c6 100644 --- a/samples/integration/typeshed.txt +++ b/samples/integration/typeshed.txt @@ -1,4 +1,5 @@ alerts +changelog create_baseline_stubs drafts generate_proto_stubs diff --git a/src/mk/tools/pre.py b/src/mk/tools/pre.py index 5bddc4a..2c2e28a 100644 --- a/src/mk/tools/pre.py +++ b/src/mk/tools/pre.py @@ -1,7 +1,10 @@ from __future__ import annotations +import datetime +import json import logging import os +import re import shutil from pathlib import Path @@ -15,32 +18,76 @@ class PreTool(Tool): def run(self, action: Action | None = None): if action: + if action.name in ["changelog"]: + self.changelog() + return run_or_fail(["pre", action.name], tee=True) def is_present(self, path: Path) -> bool: - if not os.path.exists(os.path.expanduser(CFG_FILE)): - msg = f"Multi-repo feature was disabled because {CFG_FILE} was not found." - logging.debug(msg) - return False if not shutil.which("gh"): logging.warning("Unable to find gh tool. See https://cli.github.com/") return False return True + def changelog(self) -> None: + """Pre helps you generate changelog.md from github releases.""" + releases_json = run_or_fail( + "gh api repos/{owner}/{repo}/releases", + ) + drafts_json = json.loads(releases_json.stdout) + result = """---\ntitle: Changelog\n---\n\n""" + for release in drafts_json: + logging.info("Processing release '%s'", release["tag_name"]) + result += f"# {release['tag_name']}" + if release["draft"]: + result += " (unreleased)" + else: + created = datetime.datetime.fromisoformat( + release["created_at"][:10], + ).replace( + tzinfo=datetime.timezone.utc, + ) + result += f" ({created.strftime('%Y-%m-%d')})" + + body = release["body"].strip() + result += f"\n\n{body}\n\n" + + result = result.rstrip("\r\n") + "\n" + result = result.replace("\r\n", "\n") + result = re.sub(r"\n{3,}", "\n\n", result, re.MULTILINE) + with open("CHANGELOG.md", "w", encoding="utf-8") as f: + f.write(result) + logging.info("Wrote CHANGELOG.md") + def actions(self) -> list[Action]: - # for command in app.registered_commands: - # print(command.name, command.short_help, command.short_help, command.epilog, command.short_help) - # breakpoint() - return [ - Action(name="prs", description="[dim]Show open PRs[/dim]", tool=self), + actions = [ Action( - name="drafts", - description="[dim]Show draft releases[/dim]", - tool=self, - ), - Action( - name="alerts", - description="[dim]Show dependabot security alerts[/dim]", + name="changelog", + description="[dim]Generate a changelog.md based on github releases.[/dim]", tool=self, ), ] + if not os.path.exists(os.path.expanduser(CFG_FILE)): + msg = f"Multi-repo feature was disabled because {CFG_FILE} was not found." + logging.debug(msg) + else: + actions.extend( + [ + Action( + name="prs", + description="[dim]Show open PRs[/dim]", + tool=self, + ), + Action( + name="drafts", + description="[dim]Show draft releases[/dim]", + tool=self, + ), + Action( + name="alerts", + description="[dim]Show dependabot security alerts[/dim]", + tool=self, + ), + ], + ) + return actions diff --git a/tox.ini b/tox.ini index 5b2eec2..0a5bec0 100644 --- a/tox.ini +++ b/tox.ini @@ -33,6 +33,8 @@ commands = passenv = CURL_CA_BUNDLE # https proxies, https://github.com/tox-dev/tox/issues/1437 FORCE_COLOR + GH_* + GITHUB_* HOME LANG LC_* @@ -91,7 +93,7 @@ setenv = commands = pre-commit run --all-files --show-diff-on-failure --hook-stage manual deps -[testenv:docs] +[testenv:docs,py{310,311,312,313}-docs] description = Builds docs extras = docs @@ -103,7 +105,10 @@ setenv = skip_install = false usedevelop = true commands = + sh -c "cd docs && mk changelog" mkdocs build {posargs:} +white_list_externals = + sh [testenv:lint] description = Run linters