From d528d91012a03386cb06ffb0892ba89835772c76 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Wed, 13 Oct 2021 17:07:33 +0530 Subject: [PATCH 01/27] feat(news): Add basic module setup --- poetry.lock | 25 ++++++++--- pyproject.toml | 1 + scripts/news/__init__.py | 1 + scripts/news/__main__.py | 97 ++++++++++++++++++++++++++++++++++++++++ scripts/news/utils.py | 77 +++++++++++++++++++++++++++++++ 5 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 scripts/news/__init__.py create mode 100644 scripts/news/__main__.py create mode 100644 scripts/news/utils.py diff --git a/poetry.lock b/poetry.lock index 6bf14cad..afbe6cb5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -193,7 +193,7 @@ unicode_backport = ["unicodedata2"] [[package]] name = "click" -version = "8.0.1" +version = "8.0.3" description = "Composable command line interface toolkit" category = "dev" optional = false @@ -434,7 +434,7 @@ python-versions = "*" python-dateutil = ">=2.8.1" [package.extras] -dev = ["twine", "markdown", "flake8"] +dev = ["twine", "markdown", "flake8", "wheel"] [[package]] name = "gitdb" @@ -1207,7 +1207,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "13d426f2143ed7a3fb374ff734603e7dd6e16bf259834232f9fb6c056f11f2bb" +content-hash = "71130412d7201c62c958e71e1440b48f92f9eddf74e6014d16be5e7c28c6fda4" [metadata.files] aiodns = [ @@ -1299,6 +1299,14 @@ brotlipy = [ {file = "brotlipy-0.7.0-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:79aaf217072840f3e9a3b641cccc51f7fc23037496bd71e26211856b93f4b4cb"}, {file = "brotlipy-0.7.0-cp34-cp34m-win32.whl", hash = "sha256:a07647886e24e2fb2d68ca8bf3ada398eb56fd8eac46c733d4d95c64d17f743b"}, {file = "brotlipy-0.7.0-cp34-cp34m-win_amd64.whl", hash = "sha256:c6cc0036b1304dd0073eec416cb2f6b9e37ac8296afd9e481cac3b1f07f9db25"}, + {file = "brotlipy-0.7.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:382971a641125323e90486244d6266ffb0e1f4dd920fbdcf508d2a19acc7c3b3"}, + {file = "brotlipy-0.7.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:82f61506d001e626ec3a1ac8a69df11eb3555a4878599befcb672c8178befac8"}, + {file = "brotlipy-0.7.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:7ff18e42f51ebc9d9d77a0db33f99ad95f01dd431e4491f0eca519b90e9415a9"}, + {file = "brotlipy-0.7.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:8ef230ca9e168ce2b7dc173a48a0cc3d78bcdf0bd0ea7743472a317041a4768e"}, + {file = "brotlipy-0.7.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:b7cf5bb69e767a59acc3da0d199d4b5d0c9fed7bef3ffa3efa80c6f39095686b"}, + {file = "brotlipy-0.7.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:e5c549ae5928dda952463196180445c24d6fad2d73cb13bd118293aced31b771"}, + {file = "brotlipy-0.7.0-cp35-abi3-win32.whl", hash = "sha256:79ab3bca8dd12c17e092273484f2ac48b906de2b4828dcdf6a7d520f99646ab3"}, + {file = "brotlipy-0.7.0-cp35-abi3-win_amd64.whl", hash = "sha256:ac1d66c9774ee62e762750e399a0c95e93b180e96179b645f28b162b55ae8adc"}, {file = "brotlipy-0.7.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:07194f4768eb62a4f4ea76b6d0df6ade185e24ebd85877c351daa0a069f1111a"}, {file = "brotlipy-0.7.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7e31f7adcc5851ca06134705fcf3478210da45d35ad75ec181e1ce9ce345bb38"}, {file = "brotlipy-0.7.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9448227b0df082e574c45c983fa5cd4bda7bfb11ea6b59def0940c1647be0c3c"}, @@ -1310,6 +1318,7 @@ brotlipy = [ {file = "brotlipy-0.7.0-cp36-cp36m-win32.whl", hash = "sha256:2e5c64522364a9ebcdf47c5744a5ddeb3f934742d31e61ebfbbc095460b47162"}, {file = "brotlipy-0.7.0-cp36-cp36m-win_amd64.whl", hash = "sha256:09ec3e125d16749b31c74f021aba809541b3564e5359f8c265cbae442810b41a"}, {file = "brotlipy-0.7.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:4e4638b49835d567d447a2cfacec109f9a777f219f071312268b351b6839436d"}, + {file = "brotlipy-0.7.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5664fe14f3a613431db622172bad923096a303d3adce55536f4409c8e2eafba4"}, {file = "brotlipy-0.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1379347337dc3d20b2d61456d44ccce13e0625db2611c368023b4194d5e2477f"}, {file = "brotlipy-0.7.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:22a53ccebcce2425e19f99682c12be510bf27bd75c9b77a1720db63047a77554"}, {file = "brotlipy-0.7.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4bac11c1ffba9eaa2894ec958a44e7f17778b3303c2ee9f99c39fcc511c26668"}, @@ -1363,6 +1372,11 @@ cffi = [ {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"}, {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"}, {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"}, + {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"}, + {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"}, + {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"}, + {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"}, + {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"}, {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"}, {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"}, {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"}, @@ -1410,8 +1424,8 @@ charset-normalizer = [ {file = "charset_normalizer-2.0.4-py3-none-any.whl", hash = "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b"}, ] click = [ - {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, - {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, + {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, + {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, ] codecov = [ {file = "codecov-2.1.12-py2.py3-none-any.whl", hash = "sha256:585dc217dc3d8185198ceb402f85d5cb5dbfa0c5f350a5abcdf9e347776a5b47"}, @@ -1533,6 +1547,7 @@ flake8-todo = [ ] ghp-import = [ {file = "ghp-import-2.0.1.tar.gz", hash = "sha256:753de2eace6e0f7d4edfb3cce5e3c3b98cd52aadb80163303d1d036bda7b4483"}, + {file = "ghp_import-2.0.1-py3-none-any.whl", hash = "sha256:8241a8e9f8dd3c1fafe9696e6e081b57a208ef907e9939c44e7415e407ab40ea"}, ] gitdb = [ {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"}, diff --git a/pyproject.toml b/pyproject.toml index 2a7a2de7..390e0887 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,6 +56,7 @@ pytest-xdist = { version = "^2.3.0", extras = ["psutil"] } mkdocs = ">=1.1.2,<2.0.0" mkdocs-material = ">=7.1.9,<8.0.0" mkdocs-markdownextradata-plugin = ">=0.1.7,<0.2.0" +click = "^8.0.3" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/scripts/news/__init__.py b/scripts/news/__init__.py new file mode 100644 index 00000000..f102a9ca --- /dev/null +++ b/scripts/news/__init__.py @@ -0,0 +1 @@ +__version__ = "0.0.1" diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py new file mode 100644 index 00000000..cbaf065b --- /dev/null +++ b/scripts/news/__main__.py @@ -0,0 +1,97 @@ +import atexit +import os +import shlex +import shutil +import subprocess +import tempfile + +import click + +from scripts.news.utils import NotRequiredIf, err, find_editor + +from . import __version__ + + +TEMPLATE = """ + +# Write your news/ (changelog) entry below. It should be a simple Markdown paragraph. +##################################################################################### +""".lstrip() + + +@click.group(context_settings=dict(help_option_names=["-h", "--help"])) +@click.option("-v", "--verbose", is_flag=True, help="Enables verbose mode") +@click.version_option(version=__version__) +@click.pass_context +def cli_main(ctx: click.Context, verbose: bool) -> None: + """ + Modmail News 📜🤖 + + As part of discord-modmail’s workflow, any non-trivial change to the codebase + requires an accompanying news/ file in the pull request. This submodule helps + contributors and maintainers to work with news files (changelogs) by automating + the process of generating, compiling and validating them! + """ + ... + + +@cli_main.command("add") +@click.option( + "-e", + "--editor", + default=find_editor(), + cls=NotRequiredIf, + not_required_if="message", + type=str, +) +@click.option( + "-m", + "--message", + cls=NotRequiredIf, + not_required_if="editor", +) +@click.option( + "-t", + "--type", + type=click.Choice(["feature", "bug", "maintenance"]), + prompt=True, +) +@click.pass_context +def cli_add_news(ctx: click.Context, message: str, editor: str, type: str) -> None: + """Add a changelog 📜 (news/ entry) to the current discord-modmail repo for your awesome change!""" + handle, tmp_path = tempfile.mkstemp(".md") + os.close(handle) + atexit.register(lambda: os.unlink(tmp_path)) + + def init_tmp_with_template(): + with open(tmp_path, "wt", encoding="utf-8") as file: + file.write(TEMPLATE) + + init_tmp_with_template() + + # We need to be clever about EDITOR. + # On the one hand, it might be a legitimate path to an + # executable containing spaces. + # On the other hand, it might be a partial command-line + # with options. + if shutil.which(editor): + args = [editor] + else: + args = list(shlex.split(editor)) + if not shutil.which(args[0]): + err( + f"Invalid --editor value: `{editor}`.\nIf you didn't supply it was taken from " + f"either '$GIT_EDITOR', '$EDITOR' (environment variables)" + ) + ctx.exit(1) + + args.append(tmp_path) + subprocess.run(args) + with open(tmp_path, "rt", encoding="utf-8") as file: + text = file.read() + + print(text) + + +if __name__ == "__main__": + cli_main() diff --git a/scripts/news/utils.py b/scripts/news/utils.py new file mode 100644 index 00000000..328e7995 --- /dev/null +++ b/scripts/news/utils.py @@ -0,0 +1,77 @@ +import os +import shutil +import sys +from typing import Any, Optional + +from click import Option, UsageError, echo, style + + +def _out(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: + if message is not None: + if "bold" not in styles: + styles["bold"] = True + message = style(message, **styles) + echo(message, nl=nl, err=True) + + +def _err(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: + if message is not None: + if "fg" not in styles: + styles["fg"] = "red" + message = style(message, **styles) + echo(message, nl=nl, err=True) + + +def out(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: + _out(message, nl=nl, **styles) + + +def err(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: + _err(message, nl=nl, **styles) + + +def find_editor(): + for var in "GIT_EDITOR", "EDITOR": + editor = os.environ.get(var) + if editor is not None: + return editor + if sys.platform == "win32": + fallbacks = ["notepad.exe"] + else: + fallbacks = ["/etc/alternatives/editor", "nano"] + for fallback in fallbacks: + if os.path.isabs(fallback): + found_path = fallback + else: + found_path = shutil.which(fallback) + if found_path and os.path.exists(found_path): + return found_path + err("Oh no! 💥 💔 💥 Could not find an editor! Set the EDITOR environment variable.", fg="red") + + +class NotRequiredIf(Option): + def __init__(self, *args, **kwargs): + self.not_required_if = kwargs.pop("not_required_if") + assert self.not_required_if, "'not_required_if' parameter required" + kwargs["help"] = ( + kwargs.get("help", "") + + " NOTE: This argument is mutually exclusive with %s" % self.not_required_if + ).strip() + super(NotRequiredIf, self).__init__(*args, **kwargs) + + def handle_parse_result(self, ctx, opts, args): + we_are_present = self.name in opts + other_present = self.not_required_if in opts + + if other_present: + if we_are_present: + err( + "Oh no! 💥 💔 💥 Illegal usage. `%s` is mutually exclusive with `%s`" + % (self.name, self.not_required_if), + fg="red", + ) + ctx.exit(code=1) + else: + self.prompt = None + + return super(NotRequiredIf, self).handle_parse_result(ctx, opts, args) From 19336106ec88eebc595e1810c7e1ac557dec55ab Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 14 Oct 2021 18:30:27 +0530 Subject: [PATCH 02/27] feat(news): Add command to generate news fragments --- scripts/news/__main__.py | 110 +++++++++++++++++++++++++-------------- scripts/news/utils.py | 47 ++++++++--------- 2 files changed, 91 insertions(+), 66 deletions(-) diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index cbaf065b..e7342ccd 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -1,31 +1,59 @@ -import atexit import os -import shlex -import shutil -import subprocess -import tempfile +from pathlib import Path import click -from scripts.news.utils import NotRequiredIf, err, find_editor +from scripts.news.utils import NotRequiredIf, err, nonceify, out from . import __version__ TEMPLATE = """ +# Please write your news content. When finished, save the file. +# In order to abort, exit without saving. +# Lines starting with \"#\" are ignored. -# Write your news/ (changelog) entry below. It should be a simple Markdown paragraph. -##################################################################################### """.lstrip() +class NewsFragment: + def __init__(self, ctx: click.Context, gh_pr: int, nonce: str, news_entry: str, _type: str) -> None: + self.ctx = ctx + self.gh_pr = gh_pr + self.nonce = nonce + self.news_entry = news_entry + self.news_type = _type + + def save_file(self) -> None: + """Save received changelog data to a news file.""" + path = Path(Path.cwd(), f"news/next/pr-{self.gh_pr}.{self.news_type}.{self.nonce}.md") + if not path.parent.exists(): + err( + "Oh no! 💥 💔 💥 `news/next/` doesn't exist.\nYou are either in the wrong directory while " + "running this command (should be in the project root) or the path doesn't exist, if it " + "doesn't exist please create it and run this command again :) Happy change-logging!", + fg="blue", + ) + self.ctx.exit(1) + elif path.exists(): + # The file exists + err(f"Oh no! 💥 💔 💥 {Path(os.path.relpath(path, start=Path.cwd()))} already exists") + self.ctx.exit(1) + + text = str(self.news_entry) + with open(path, "wt", encoding="utf-8") as file: + file.write(text) + + out(f"All done! ✨ 🍰 ✨ Created news fragment at {Path(os.path.relpath(path, start=Path.cwd()))}") + + @click.group(context_settings=dict(help_option_names=["-h", "--help"])) @click.option("-v", "--verbose", is_flag=True, help="Enables verbose mode") @click.version_option(version=__version__) @click.pass_context def cli_main(ctx: click.Context, verbose: bool) -> None: """ - Modmail News 📜🤖 + Modmail News 📜🤖. As part of discord-modmail’s workflow, any non-trivial change to the codebase requires an accompanying news/ file in the pull request. This submodule helps @@ -39,7 +67,6 @@ def cli_main(ctx: click.Context, verbose: bool) -> None: @click.option( "-e", "--editor", - default=find_editor(), cls=NotRequiredIf, not_required_if="message", type=str, @@ -56,41 +83,44 @@ def cli_main(ctx: click.Context, verbose: bool) -> None: type=click.Choice(["feature", "bug", "maintenance"]), prompt=True, ) +@click.option( + "--pr-number", + type=int, + prompt=True, +) @click.pass_context -def cli_add_news(ctx: click.Context, message: str, editor: str, type: str) -> None: +def cli_add_news(ctx: click.Context, message: str, editor: str, type: str, pr_number: int) -> None: """Add a changelog 📜 (news/ entry) to the current discord-modmail repo for your awesome change!""" - handle, tmp_path = tempfile.mkstemp(".md") - os.close(handle) - atexit.register(lambda: os.unlink(tmp_path)) - - def init_tmp_with_template(): - with open(tmp_path, "wt", encoding="utf-8") as file: - file.write(TEMPLATE) - - init_tmp_with_template() - - # We need to be clever about EDITOR. - # On the one hand, it might be a legitimate path to an - # executable containing spaces. - # On the other hand, it might be a partial command-line - # with options. - if shutil.which(editor): - args = [editor] - else: - args = list(shlex.split(editor)) - if not shutil.which(args[0]): - err( - f"Invalid --editor value: `{editor}`.\nIf you didn't supply it was taken from " - f"either '$GIT_EDITOR', '$EDITOR' (environment variables)" + if not message: + message_notes = [] + while True: + content = click.edit( + ( + "# Please write your news content. When finished, save the file.\n" + "# In order to abort, exit without saving.\n" + "# Lines starting with '#' are ignored.\n" + "\n".join(message_notes) + ), + editor=editor, + extension="md", ) - ctx.exit(1) - args.append(tmp_path) - subprocess.run(args) - with open(tmp_path, "rt", encoding="utf-8") as file: - text = file.read() + if not content: + message_notes = "# ERROR: No content found previously" + continue + + message = "\n".join( + [line.rstrip() for line in content.split("\n") if not line.lstrip().startswith("#")] + ) + + if message is None: + out("Aborting creating new news fragment/changelog!", fg="yellow") + ctx.exit(1) + + break - print(text) + news_fragment = NewsFragment(ctx, pr_number, nonceify(message), message, type) + news_fragment.save_file() if __name__ == "__main__": diff --git a/scripts/news/utils.py b/scripts/news/utils.py index 328e7995..34ea7579 100644 --- a/scripts/news/utils.py +++ b/scripts/news/utils.py @@ -1,9 +1,21 @@ -import os -import shutil -import sys -from typing import Any, Optional +import base64 +import hashlib +from typing import Any, List, Mapping, Optional -from click import Option, UsageError, echo, style +import click +from click import Option, echo, style + + +def nonceify(body: str) -> str: + """ + Nonceify the changelog body! + + Generate hopefully-unique string of characters meant to prevent filename collisions. by computing the + MD5 hash of the text, converting it to base64 (using the "urlsafe" alphabet), and taking the first + 6 characters of that. + """ + digest = hashlib.md5(body.encode("utf-8")).digest() # noqa: S303 + return base64.urlsafe_b64encode(digest)[0:6].decode("ascii") def _out(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: @@ -23,43 +35,26 @@ def _err(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: def out(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: + """Utility function to output a styled message to console.""" _out(message, nl=nl, **styles) def err(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: + """Utility function to output a styled error message to console.""" _err(message, nl=nl, **styles) -def find_editor(): - for var in "GIT_EDITOR", "EDITOR": - editor = os.environ.get(var) - if editor is not None: - return editor - if sys.platform == "win32": - fallbacks = ["notepad.exe"] - else: - fallbacks = ["/etc/alternatives/editor", "nano"] - for fallback in fallbacks: - if os.path.isabs(fallback): - found_path = fallback - else: - found_path = shutil.which(fallback) - if found_path and os.path.exists(found_path): - return found_path - err("Oh no! 💥 💔 💥 Could not find an editor! Set the EDITOR environment variable.", fg="red") - - class NotRequiredIf(Option): def __init__(self, *args, **kwargs): self.not_required_if = kwargs.pop("not_required_if") - assert self.not_required_if, "'not_required_if' parameter required" + assert self.not_required_if, "'not_required_if' parameter required" # noqa: S101 kwargs["help"] = ( kwargs.get("help", "") + " NOTE: This argument is mutually exclusive with %s" % self.not_required_if ).strip() super(NotRequiredIf, self).__init__(*args, **kwargs) - def handle_parse_result(self, ctx, opts, args): + def handle_parse_result(self, ctx: click.Context, opts: Mapping[str, Any], args: List[str]): we_are_present = self.name in opts other_present = self.not_required_if in opts From a58baee07863b2b041a02518a780a753cf9447ae Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Fri, 15 Oct 2021 07:01:37 +0530 Subject: [PATCH 03/27] feat: Add validation for github PR number --- news/next/pr-65.bug.RFwfXL.md | 1 + scripts/news/__init__.py | 2 ++ scripts/news/__main__.py | 35 ++++++++++++++++++++++++++++++++--- scripts/news/utils.py | 4 +++- 4 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 news/next/pr-65.bug.RFwfXL.md diff --git a/news/next/pr-65.bug.RFwfXL.md b/news/next/pr-65.bug.RFwfXL.md new file mode 100644 index 00000000..73e3505a --- /dev/null +++ b/news/next/pr-65.bug.RFwfXL.md @@ -0,0 +1 @@ +Add database client diff --git a/scripts/news/__init__.py b/scripts/news/__init__.py index f102a9ca..69a40730 100644 --- a/scripts/news/__init__.py +++ b/scripts/news/__init__.py @@ -1 +1,3 @@ +ERROR_MSG_PREFIX = "Oh no! 💥 💔 💥" + __version__ = "0.0.1" diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index e7342ccd..ce783c6c 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -1,13 +1,21 @@ import os from pathlib import Path +from typing import Optional import click +import requests from scripts.news.utils import NotRequiredIf, err, nonceify, out -from . import __version__ +from . import ERROR_MSG_PREFIX, __version__ +PR_ENDPOINT = "https://api.github.com/repos/discord-modmail/modmail/pulls/{number}" +BAD_RESPONSE = { + 404: "Pull request not located! Please enter a valid number!", + 403: "Rate limit has been hit! Please try again later!", +} + TEMPLATE = """ # Please write your news content. When finished, save the file. # In order to abort, exit without saving. @@ -29,7 +37,7 @@ def save_file(self) -> None: path = Path(Path.cwd(), f"news/next/pr-{self.gh_pr}.{self.news_type}.{self.nonce}.md") if not path.parent.exists(): err( - "Oh no! 💥 💔 💥 `news/next/` doesn't exist.\nYou are either in the wrong directory while " + f"{ERROR_MSG_PREFIX} `news/next/` doesn't exist.\nYou are either in the wrong directory while " "running this command (should be in the project root) or the path doesn't exist, if it " "doesn't exist please create it and run this command again :) Happy change-logging!", fg="blue", @@ -37,7 +45,7 @@ def save_file(self) -> None: self.ctx.exit(1) elif path.exists(): # The file exists - err(f"Oh no! 💥 💔 💥 {Path(os.path.relpath(path, start=Path.cwd()))} already exists") + err(f"{ERROR_MSG_PREFIX} {Path(os.path.relpath(path, start=Path.cwd()))} already exists") self.ctx.exit(1) text = str(self.news_entry) @@ -47,6 +55,26 @@ def save_file(self) -> None: out(f"All done! ✨ 🍰 ✨ Created news fragment at {Path(os.path.relpath(path, start=Path.cwd()))}") +def validate_pull_request_number( + ctx: click.Context, param: click.Parameter, value: Optional[int] +) -> Optional[int]: + r = requests.get(PR_ENDPOINT.format(number=value)) + if r.status_code == 403: + if r.headers.get("X-RateLimit-Remaining") == "0": + err(f"{ERROR_MSG_PREFIX} Ratelimit reached, please retry in a few minutes.") + ctx.exit() + err(f"{ERROR_MSG_PREFIX} Cannot access pull request.") + ctx.exit() + elif r.status_code in (404, 410): + err(f"{ERROR_MSG_PREFIX} PR not found.") + ctx.exit() + elif r.status_code != 200: + err(f"{ERROR_MSG_PREFIX} Error while fetching issue, retry again after sometime.") + ctx.exit() + + return value + + @click.group(context_settings=dict(help_option_names=["-h", "--help"])) @click.option("-v", "--verbose", is_flag=True, help="Enables verbose mode") @click.version_option(version=__version__) @@ -87,6 +115,7 @@ def cli_main(ctx: click.Context, verbose: bool) -> None: "--pr-number", type=int, prompt=True, + callback=validate_pull_request_number, ) @click.pass_context def cli_add_news(ctx: click.Context, message: str, editor: str, type: str, pr_number: int) -> None: diff --git a/scripts/news/utils.py b/scripts/news/utils.py index 34ea7579..ba12af9f 100644 --- a/scripts/news/utils.py +++ b/scripts/news/utils.py @@ -5,6 +5,8 @@ import click from click import Option, echo, style +from . import ERROR_MSG_PREFIX + def nonceify(body: str) -> str: """ @@ -61,7 +63,7 @@ def handle_parse_result(self, ctx: click.Context, opts: Mapping[str, Any], args: if other_present: if we_are_present: err( - "Oh no! 💥 💔 💥 Illegal usage. `%s` is mutually exclusive with `%s`" + f"{ERROR_MSG_PREFIX} Illegal usage. `%s` is mutually exclusive with `%s`" % (self.name, self.not_required_if), fg="red", ) From 7288e07584ee668dd648c9ea5154a861231b67d7 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Fri, 15 Oct 2021 07:07:31 +0530 Subject: [PATCH 04/27] deps: Add jinja2 for changelog templates --- poetry.lock | 8 ++++---- pyproject.toml | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index afbe6cb5..b4eaa0ae 100644 --- a/poetry.lock +++ b/poetry.lock @@ -529,7 +529,7 @@ plugins = ["setuptools"] [[package]] name = "jinja2" -version = "3.0.1" +version = "3.0.2" description = "A very fast and expressive template engine." category = "dev" optional = false @@ -1207,7 +1207,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "71130412d7201c62c958e71e1440b48f92f9eddf74e6014d16be5e7c28c6fda4" +content-hash = "e99a8dc25d0c2d30dd939af8e60aa92c8e4851614508fb46387d0663a0a8c261" [metadata.files] aiodns = [ @@ -1582,8 +1582,8 @@ isort = [ {file = "isort-5.9.3.tar.gz", hash = "sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899"}, ] jinja2 = [ - {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"}, - {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"}, + {file = "Jinja2-3.0.2-py3-none-any.whl", hash = "sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c"}, + {file = "Jinja2-3.0.2.tar.gz", hash = "sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45"}, ] markdown = [ {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, diff --git a/pyproject.toml b/pyproject.toml index 390e0887..aa3423f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,7 @@ mkdocs = ">=1.1.2,<2.0.0" mkdocs-material = ">=7.1.9,<8.0.0" mkdocs-markdownextradata-plugin = ">=0.1.7,<0.2.0" click = "^8.0.3" +Jinja2 = "^3.0.2" [build-system] requires = ["poetry-core>=1.0.0"] From 70e0b70bd5df15d2b9cec4130ad6099285f78e7f Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sat, 16 Oct 2021 06:45:58 +0530 Subject: [PATCH 05/27] WIP build news support - form metadata from news/ directory --- .../2021-10-16.pr-77.bugfixes.2OV3m_.md | 1 + .../2021-10-16.pr-13.features.nrKe2p.md | 3 + .../2021-10-16.pr-19.features.SqoIZV.md | 1 + .../2021-10-16.pr-50.features.6zI2Dq.md | 1 + .../2021-10-16.pr-70.internal.-pHKF1.md | 1 + news/next/pr-65.bug.RFwfXL.md | 1 - scripts/news/__main__.py | 99 +++++++++++++++--- .../news/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 237 bytes .../news/__pycache__/__main__.cpython-39.pyc | Bin 0 -> 6955 bytes scripts/news/__pycache__/utils.cpython-39.pyc | Bin 0 -> 4581 bytes scripts/news/utils.py | 52 ++++++++- 11 files changed, 145 insertions(+), 14 deletions(-) create mode 100644 news/next/bugfixes/2021-10-16.pr-77.bugfixes.2OV3m_.md create mode 100644 news/next/features/2021-10-16.pr-13.features.nrKe2p.md create mode 100644 news/next/features/2021-10-16.pr-19.features.SqoIZV.md create mode 100644 news/next/features/2021-10-16.pr-50.features.6zI2Dq.md create mode 100644 news/next/internal/2021-10-16.pr-70.internal.-pHKF1.md delete mode 100644 news/next/pr-65.bug.RFwfXL.md create mode 100644 scripts/news/__pycache__/__init__.cpython-39.pyc create mode 100644 scripts/news/__pycache__/__main__.cpython-39.pyc create mode 100644 scripts/news/__pycache__/utils.cpython-39.pyc diff --git a/news/next/bugfixes/2021-10-16.pr-77.bugfixes.2OV3m_.md b/news/next/bugfixes/2021-10-16.pr-77.bugfixes.2OV3m_.md new file mode 100644 index 00000000..e36cb8b0 --- /dev/null +++ b/news/next/bugfixes/2021-10-16.pr-77.bugfixes.2OV3m_.md @@ -0,0 +1 @@ +Errors no longer happen silently and notify the user when they make a mistake. diff --git a/news/next/features/2021-10-16.pr-13.features.nrKe2p.md b/news/next/features/2021-10-16.pr-13.features.nrKe2p.md new file mode 100644 index 00000000..905071a8 --- /dev/null +++ b/news/next/features/2021-10-16.pr-13.features.nrKe2p.md @@ -0,0 +1,3 @@ +Add docker setup - docker-compose.yml + +Running the bot after configuring the env vars is now as simple as `docker-compose up`. diff --git a/news/next/features/2021-10-16.pr-19.features.SqoIZV.md b/news/next/features/2021-10-16.pr-19.features.SqoIZV.md new file mode 100644 index 00000000..7b812ef3 --- /dev/null +++ b/news/next/features/2021-10-16.pr-19.features.SqoIZV.md @@ -0,0 +1 @@ +Automatic docker image creation at `ghcr.io/discord-modmail/modmail` diff --git a/news/next/features/2021-10-16.pr-50.features.6zI2Dq.md b/news/next/features/2021-10-16.pr-50.features.6zI2Dq.md new file mode 100644 index 00000000..901be656 --- /dev/null +++ b/news/next/features/2021-10-16.pr-50.features.6zI2Dq.md @@ -0,0 +1 @@ +Interaction Paginator that uses discord buttons. diff --git a/news/next/internal/2021-10-16.pr-70.internal.-pHKF1.md b/news/next/internal/2021-10-16.pr-70.internal.-pHKF1.md new file mode 100644 index 00000000..88359b66 --- /dev/null +++ b/news/next/internal/2021-10-16.pr-70.internal.-pHKF1.md @@ -0,0 +1 @@ +Code style: two blank lines after imports instead of one. diff --git a/news/next/pr-65.bug.RFwfXL.md b/news/next/pr-65.bug.RFwfXL.md deleted file mode 100644 index 73e3505a..00000000 --- a/news/next/pr-65.bug.RFwfXL.md +++ /dev/null @@ -1 +0,0 @@ -Add database client diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index ce783c6c..69462c49 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -1,13 +1,16 @@ import os +import subprocess +from collections import defaultdict +from datetime import datetime, timezone from pathlib import Path +from pprint import pprint from typing import Optional import click import requests -from scripts.news.utils import NotRequiredIf, err, nonceify, out - from . import ERROR_MSG_PREFIX, __version__ +from .utils import NotRequiredIf, err, get_metadata_from_file, glob_fragments, nonceify, out PR_ENDPOINT = "https://api.github.com/repos/discord-modmail/modmail/pulls/{number}" @@ -22,6 +25,13 @@ # Lines starting with \"#\" are ignored. """.lstrip() +NO_NEWS_PATH_ERROR = ( + f"{ERROR_MSG_PREFIX} `news/next/` doesn't exist.\nYou are either in the wrong directory while" + " running this command (should be in the project root) or the path doesn't exist, if it " + "doesn't exist please create it and run this command again :) Happy change-logging!" +) + +SECTIONS = ["Security", "Documentation", "Tests", "Features", "Internal", "BugFixes"] class NewsFragment: @@ -31,18 +41,23 @@ def __init__(self, ctx: click.Context, gh_pr: int, nonce: str, news_entry: str, self.nonce = nonce self.news_entry = news_entry self.news_type = _type + self.date = datetime.now(timezone.utc).strftime("%Y-%m-%d") def save_file(self) -> None: """Save received changelog data to a news file.""" - path = Path(Path.cwd(), f"news/next/pr-{self.gh_pr}.{self.news_type}.{self.nonce}.md") - if not path.parent.exists(): - err( - f"{ERROR_MSG_PREFIX} `news/next/` doesn't exist.\nYou are either in the wrong directory while " - "running this command (should be in the project root) or the path doesn't exist, if it " - "doesn't exist please create it and run this command again :) Happy change-logging!", - fg="blue", - ) + path = Path( + Path.cwd(), + f"news/next/{self.news_type}/{self.date}.pr-{self.gh_pr}.{self.news_type}.{self.nonce}.md", + ) + if not path.parents[1].exists(): + err(NO_NEWS_PATH_ERROR, fg="blue") self.ctx.exit(1) + elif not path.parents[0].exists(): + make_news_type_dir = click.confirm( + f"Should I make the new type DIR for the news type at {path.parents[0]}" + ) + if make_news_type_dir: + path.parents[0].mkdir(exist_ok=True) elif path.exists(): # The file exists err(f"{ERROR_MSG_PREFIX} {Path(os.path.relpath(path, start=Path.cwd()))} already exists") @@ -52,7 +67,13 @@ def save_file(self) -> None: with open(path, "wt", encoding="utf-8") as file: file.write(text) - out(f"All done! ✨ 🍰 ✨ Created news fragment at {Path(os.path.relpath(path, start=Path.cwd()))}") + # Add news fragment to git stage + subprocess.run(["git", "add", "--force", path]).check_returncode() + + out( + f"All done! ✨ 🍰 ✨ Created news fragment at {Path(os.path.relpath(path, start=Path.cwd()))}" + "\nYou are now ready for commit!" + ) def validate_pull_request_number( @@ -108,7 +129,7 @@ def cli_main(ctx: click.Context, verbose: bool) -> None: @click.option( "-t", "--type", - type=click.Choice(["feature", "bug", "maintenance"]), + type=click.Choice([section.lower() for section in SECTIONS]), prompt=True, ) @click.option( @@ -152,5 +173,59 @@ def cli_add_news(ctx: click.Context, message: str, editor: str, type: str, pr_nu news_fragment.save_file() +@cli_main.command("build") +@click.option( + "--version", + type=str, +) +@click.option( + "-f", + "--force", + is_flag=True, + default=False, + help="Do not ask for confirmation to remove news fragments.", +) +@click.option( + "-t", + "--template", + type=click.Path( + exists=True, + file_okay=True, + dir_okay=False, + readable=True, + allow_dash=False, + path_type=str, + ), + is_eager=True, + help="Read JINJA2 template from FILE path.", +) +@click.pass_context +def cli_build_news(ctx: click.Context, version: str, force: bool, template: str) -> None: + """Build a combined news file 📜 from news fragments.""" + filenames = glob_fragments("next", SECTIONS) + _file_metadata = {} + file_metadata = defaultdict(list) + date = datetime.now(timezone.utc).strftime("%Y-%m-%d") + + if not filenames: + err(f"No news fragments found. Exiting") + ctx.exit() + else: + for filename in filenames: + if not filename.endswith(".md"): + continue + _file_metadata[filename] = get_metadata_from_file(Path(filename)) + + # Group metadata according to news_type + for path, fragment in _file_metadata.items(): + news_type = fragment["news_type"] + del fragment["news_type"] + fragment["path"] = path + file_metadata[news_type].append(fragment) + + file_metadata["release date"] = date + pprint(file_metadata) + + if __name__ == "__main__": cli_main() diff --git a/scripts/news/__pycache__/__init__.cpython-39.pyc b/scripts/news/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a93148275d334188e5cd96e99a22729fc58c0fd GIT binary patch literal 237 zcmYe~<>g`k0`Z#}iB3TJF^Gc<7=auIATH(s5-AK(3@MCJj44dP44TZPqCg@4428UW zMTHOZCoKgLQ^2e$Rs%f)JwrcDrYHf|pdkOCc;8_6_<$f+H_wP$-0|^csYS(^`FZj2 zD;bKIfQEpHU*7r|`MIh3#Tl7piFw5t`T<4x=|zdTxtV$C`URCG89)sMMfq8&$tA`5 zx%nx%iJ3Y2#mPmP1wejYYI(7Ke0*kJW=VX!UP0w84jZ77(wtN~kOzuEmas4Z0LbJ* A&;S4c literal 0 HcmV?d00001 diff --git a/scripts/news/__pycache__/__main__.cpython-39.pyc b/scripts/news/__pycache__/__main__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71eb97f6fb563eca47310aa62348815f6efe4134 GIT binary patch literal 6955 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 List[str]: + filenames = [] + base = os.path.join("news", version) + + if version != "next": + wildcard = base + ".md" + filenames.extend(glob.glob(wildcard)) + else: + for section in sections: + wildcard = os.path.join(base, sanitize_section(section), "*.md") + entries = glob.glob(wildcard) + entries.sort(reverse=True) + deletables = [x for x in entries if x.endswith("/README.md")] + for filename in deletables: + entries.remove(filename) + filenames.extend(entries) + + return filenames + + +def get_metadata_from_file(path: Path) -> dict: + # path = Path(Path.cwd(), f"news/next/pr-{self.gh_pr}.{self.news_type}.{self.nonce}.md") + new_fragment_file = path.stem + date, gh_pr, news_type, nonce = new_fragment_file.split(".") + + with open(path, "r", encoding="utf-8") as file: + news_entry = file.read() + + metadata = {"date": date, "gh_pr": gh_pr, "news_type": news_type, "nonce": nonce, "new_entry": news_entry} + return metadata + + +def get_project_meta() -> Tuple[str, str]: + with open("pyproject.toml", "rb") as pyproject: + file_contents = tomli.load(pyproject) + + version = file_contents["tool"]["poetry"]["version"] + name = file_contents["tool"]["poetry"]["name"] + return name, version From 7a1b75bcedfa79164e3443f72cab512149f1aaad Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sun, 17 Oct 2021 05:35:37 +0530 Subject: [PATCH 06/27] feat(news): Add TOML configuration support --- .gitignore | 1 + .../2021-10-16.pr-77.bugfixes.2OV3m_.md | 1 - .../2021-10-16.pr-13.features.nrKe2p.md | 3 -- .../2021-10-16.pr-19.features.SqoIZV.md | 1 - .../2021-10-16.pr-50.features.6zI2Dq.md | 1 - .../2021-10-16.pr-70.internal.-pHKF1.md | 1 - scripts/news/__main__.py | 13 ++++---- .../news/__pycache__/__main__.cpython-39.pyc | Bin 6955 -> 6980 bytes scripts/news/__pycache__/utils.cpython-39.pyc | Bin 4581 -> 5390 bytes scripts/news/default_config.toml | 15 ++++++++++ scripts/news/utils.py | 28 +++++++++++++++++- 11 files changed, 49 insertions(+), 15 deletions(-) delete mode 100644 news/next/bugfixes/2021-10-16.pr-77.bugfixes.2OV3m_.md delete mode 100644 news/next/features/2021-10-16.pr-13.features.nrKe2p.md delete mode 100644 news/next/features/2021-10-16.pr-19.features.SqoIZV.md delete mode 100644 news/next/features/2021-10-16.pr-50.features.6zI2Dq.md delete mode 100644 news/next/internal/2021-10-16.pr-70.internal.-pHKF1.md create mode 100644 scripts/news/default_config.toml diff --git a/.gitignore b/.gitignore index 523110e0..5a267be8 100644 --- a/.gitignore +++ b/.gitignore @@ -137,6 +137,7 @@ logs # Configuration *config.toml +!scripts/news/default_config.toml # Custom docker compose override docker-compose.override.yml diff --git a/news/next/bugfixes/2021-10-16.pr-77.bugfixes.2OV3m_.md b/news/next/bugfixes/2021-10-16.pr-77.bugfixes.2OV3m_.md deleted file mode 100644 index e36cb8b0..00000000 --- a/news/next/bugfixes/2021-10-16.pr-77.bugfixes.2OV3m_.md +++ /dev/null @@ -1 +0,0 @@ -Errors no longer happen silently and notify the user when they make a mistake. diff --git a/news/next/features/2021-10-16.pr-13.features.nrKe2p.md b/news/next/features/2021-10-16.pr-13.features.nrKe2p.md deleted file mode 100644 index 905071a8..00000000 --- a/news/next/features/2021-10-16.pr-13.features.nrKe2p.md +++ /dev/null @@ -1,3 +0,0 @@ -Add docker setup - docker-compose.yml - -Running the bot after configuring the env vars is now as simple as `docker-compose up`. diff --git a/news/next/features/2021-10-16.pr-19.features.SqoIZV.md b/news/next/features/2021-10-16.pr-19.features.SqoIZV.md deleted file mode 100644 index 7b812ef3..00000000 --- a/news/next/features/2021-10-16.pr-19.features.SqoIZV.md +++ /dev/null @@ -1 +0,0 @@ -Automatic docker image creation at `ghcr.io/discord-modmail/modmail` diff --git a/news/next/features/2021-10-16.pr-50.features.6zI2Dq.md b/news/next/features/2021-10-16.pr-50.features.6zI2Dq.md deleted file mode 100644 index 901be656..00000000 --- a/news/next/features/2021-10-16.pr-50.features.6zI2Dq.md +++ /dev/null @@ -1 +0,0 @@ -Interaction Paginator that uses discord buttons. diff --git a/news/next/internal/2021-10-16.pr-70.internal.-pHKF1.md b/news/next/internal/2021-10-16.pr-70.internal.-pHKF1.md deleted file mode 100644 index 88359b66..00000000 --- a/news/next/internal/2021-10-16.pr-70.internal.-pHKF1.md +++ /dev/null @@ -1 +0,0 @@ -Code style: two blank lines after imports instead of one. diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index 69462c49..10bae277 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -3,14 +3,13 @@ from collections import defaultdict from datetime import datetime, timezone from pathlib import Path -from pprint import pprint from typing import Optional import click import requests from . import ERROR_MSG_PREFIX, __version__ -from .utils import NotRequiredIf, err, get_metadata_from_file, glob_fragments, nonceify, out +from .utils import NotRequiredIf, err, get_metadata_from_file, glob_fragments, load_toml_config, nonceify, out PR_ENDPOINT = "https://api.github.com/repos/discord-modmail/modmail/pulls/{number}" @@ -31,7 +30,9 @@ "doesn't exist please create it and run this command again :) Happy change-logging!" ) -SECTIONS = ["Security", "Documentation", "Tests", "Features", "Internal", "BugFixes"] + +CONFIG = load_toml_config() +SECTIONS = [config["name"] for _type, config in CONFIG.get("types").items()] class NewsFragment: @@ -96,11 +97,10 @@ def validate_pull_request_number( return value -@click.group(context_settings=dict(help_option_names=["-h", "--help"])) -@click.option("-v", "--verbose", is_flag=True, help="Enables verbose mode") +@click.group(context_settings=dict(help_option_names=["-h", "--help"]), invoke_without_command=True) @click.version_option(version=__version__) @click.pass_context -def cli_main(ctx: click.Context, verbose: bool) -> None: +def cli_main(ctx: click.Context) -> None: """ Modmail News 📜🤖. @@ -224,7 +224,6 @@ def cli_build_news(ctx: click.Context, version: str, force: bool, template: str) file_metadata[news_type].append(fragment) file_metadata["release date"] = date - pprint(file_metadata) if __name__ == "__main__": diff --git a/scripts/news/__pycache__/__main__.cpython-39.pyc b/scripts/news/__pycache__/__main__.cpython-39.pyc index 71eb97f6fb563eca47310aa62348815f6efe4134..f2fe9e2276dac5681bfca07e652ecb79607bfe76 100644 GIT binary patch delta 2241 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 diff --git a/scripts/news/__pycache__/utils.cpython-39.pyc b/scripts/news/__pycache__/utils.cpython-39.pyc index 176bfc894bb09a59d49e515b151dbd6e425744d7..c33a4bd08ce6f8c4b6374dd626ef0f76ecada099 100644 GIT binary patch delta 2355 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 diff --git a/scripts/news/default_config.toml b/scripts/news/default_config.toml new file mode 100644 index 00000000..d735e4e2 --- /dev/null +++ b/scripts/news/default_config.toml @@ -0,0 +1,15 @@ +[core] +sections = ["Security", "Documentation", "Tests", "Features", "Internal", "BugFixes"] +template = "default_template.rst" +start_string = "# Changelog\nAll notable changes to this project will be documented in this file." +title_format = "{name} {version} ({project_date})" +pr_format = "#{pr} " + +[types] +feature = { name = "Features" } +trivial = { name = "Trivial/Internal Changes" } +improvement = { name = "Improvements" } +bugfix = { name = "Bug Fixes" } +doc = { name = "Improved Documentation" } +deprecation = { name = "Deprecations" } +breaking = { name = "Breaking Changes" } diff --git a/scripts/news/utils.py b/scripts/news/utils.py index 8bcfb639..21ad91b1 100644 --- a/scripts/news/utils.py +++ b/scripts/news/utils.py @@ -2,8 +2,10 @@ import glob import hashlib import os +import sys +import traceback from pathlib import Path -from typing import Any, List, Mapping, Optional, Tuple +from typing import Any, Dict, List, Mapping, Optional, Tuple import click import tomli @@ -122,3 +124,27 @@ def get_project_meta() -> Tuple[str, str]: version = file_contents["tool"]["poetry"]["version"] name = file_contents["tool"]["poetry"]["name"] return name, version + + +def load_toml_config() -> Dict[str, Any]: + config_path = Path(Path.cwd(), "scripts/news/config.toml") + default_config_url = "URL HERE PLEASE" + + if not config_path.exists(): + err( + f"Configuration not found. Create a config file at '{config_path}', and see " + f"'{default_config_url}' for an example configuration. " + ) + sys.exit(1) + try: + with open(config_path, mode="r") as file: + toml_dict = tomli.loads(file.read()) + except tomli.TOMLDecodeError as e: + message = "Invalid changelog news configuration at {}\n{}".format( + config_path, + "".join(traceback.format_exception_only(type(e), e)), + ) + err(message) + sys.exit(1) + else: + return toml_dict From 5440573ade067d6f6bacc5ba1d225581e64dff8b Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sun, 17 Oct 2021 13:07:11 +0530 Subject: [PATCH 07/27] Add support for compiling news into version changelog --- scripts/news/__main__.py | 42 ++++++++++++- .../news/__pycache__/__main__.cpython-39.pyc | Bin 6980 -> 7434 bytes scripts/news/__pycache__/utils.cpython-39.pyc | Bin 5390 -> 6456 bytes scripts/news/default_config.toml | 4 -- scripts/news/default_template.rst.jinja | 16 +++++ scripts/news/utils.py | 56 ++++++++++++++++-- 6 files changed, 106 insertions(+), 12 deletions(-) create mode 100644 scripts/news/default_template.rst.jinja diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index 10bae277..963a6145 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -9,7 +9,17 @@ import requests from . import ERROR_MSG_PREFIX, __version__ -from .utils import NotRequiredIf, err, get_metadata_from_file, glob_fragments, load_toml_config, nonceify, out +from .utils import ( + NotRequiredIf, + err, + get_metadata_from_file, + get_project_meta, + glob_fragments, + load_toml_config, + nonceify, + out, + render_fragments, +) PR_ENDPOINT = "https://api.github.com/repos/discord-modmail/modmail/pulls/{number}" @@ -32,7 +42,7 @@ CONFIG = load_toml_config() -SECTIONS = [config["name"] for _type, config in CONFIG.get("types").items()] +SECTIONS = [_type for _type, _ in CONFIG.get("types").items()] class NewsFragment: @@ -223,7 +233,33 @@ def cli_build_news(ctx: click.Context, version: str, force: bool, template: str) fragment["path"] = path file_metadata[news_type].append(fragment) - file_metadata["release date"] = date + template = CONFIG["core"].get("template") + if not template: + template = Path(Path.cwd(), "scripts/news/default_template.rst.jinja") + else: + template = Path(Path.cwd(), f"scripts/news/{template}") + + if not template.exists(): + err( + f"{ERROR_MSG_PREFIX} Template at {template.relative_to(Path.cwd())} not found :(. Make sure " + f"your path is relative to `scripts/news`!" + ) + + name, version = get_project_meta() + changelog = render_fragments( + section_names=CONFIG["types"], + template=template, + metadata=file_metadata, + wrap=True, + version_data=(name, version), + date=date, + ) + changelog_path = Path(Path.cwd(), f"news/{version}.md") + + with open(changelog_path, mode="w") as filename: + filename.write(changelog) + + out(f"All done! ✨ 🍰 ✨ Created changelog at {changelog_path}") if __name__ == "__main__": diff --git a/scripts/news/__pycache__/__main__.cpython-39.pyc b/scripts/news/__pycache__/__main__.cpython-39.pyc index f2fe9e2276dac5681bfca07e652ecb79607bfe76..101eb7ae5c5aa0371782198b9555eb41f0a8b43d 100644 GIT binary patch delta 2104 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 I-PXtd1KrV}FaQ7m delta 1879 zcmZ`)%WvF7829+K_O89_%|4QBk|xcg%_glNq3VN%G(1ERG$m3iTKQtRJCk%9dmYWV zkH8^91eF6+Dsuwm2A3WHpazXHYP8vd^uH~O+zTfwn`M#O`VgAv~ zZ0Nd01%8dyJK+~KMfn*ghkqtamf#il9?zcLQX?}i_ywX+g=&!%+rAwa{bF45OR?iS zvFp2W*)Qiji7I{t^ho6S9(?tv5?B3d+~@bjHNOTr1~sF4-0&MjVNKSgg&mbz)ZW(p zemcQi)*MvW0JABfr5&9*)ZN-P{6RW-geubtFfBTDgz;zPf$ZeDr*ccsxT!-lh%#X7&X1}TqD)iV6 zp`&yRq~ojzQu{!9oQ{KZf|6@W`{Zx9!&loR(^taucH5MWI;*xdxuiCB@2I~Mw+uXQ z0z6T8ONRQ5Q&pG<9YC?_gb%~_dRxy5F%u$OW0~5EGE=0R5fi*3cZ?Nz#u!*R25Onc zIDew@5{x<4`iaIzaEv4$MQ9zs5R5WCNbaVnBJ$QCR{nT|Pl4m^X-5cBXpH653a z3nOGnepeWrzGE;GYGq*!n9T}MA8ki_sH_sIQ)Aoliw_lD@k_gZ6bK=+@=vRu%t*_g ztK@6Kz2NBkynM@EAZO&q_K(R)n8Q*|Am`-cOz$L=we3vrg)!r^$Y5671;}*vpqpmq z22bLRl+U4j2IaNeVUI>E*a*2`0cYZFl(tR&5-OZWxPWj*eqWrZpT*&SDJ{bERrzc2 zP2$KGN>db5%#2Rd?W|{o*OOk#9;6(PZKjEoa}BKx1T@UtAm~J)5FHjpL2#&gd6az3 znBX~h^LDd{WAhpLP3gzXC$OEU%PD$=fn2PmDg} zrMwm;w}Mq3uEngEiktG=@{}85Dk{H$wiWqP`7`3lUgbr@w|nfq{JJu^g_K`~*@0=W z+Km`zXtIj1hHx7o(~-%FpezE2!8bE2W@$*nG(5DK`8jH?u{3x}g6kyC-A3R>VDBiZ zN%~Ze$R*FPHee2oRgufyS8l$}izw0fy8PQa-8hgO4k9mA8;u8`wtr9^G7awFpr*Us z>ZhcLL3jt<$bb6Iy^Ke!?QyJ7rgiSq%!K9?smR+Enq^b)1+-X4;HYIpNk|2c9>+3+PhlvOQMZTe*UnHIkpkU z4#;#&m*Y0`6ajNPw3&JSdD#}Ng*c&iBeqCDMVtcIy8-vmA!90z>Z+r8PRoMFXc^wH LWwh#^)3pBu)S-^r diff --git a/scripts/news/default_config.toml b/scripts/news/default_config.toml index d735e4e2..c300c997 100644 --- a/scripts/news/default_config.toml +++ b/scripts/news/default_config.toml @@ -1,9 +1,5 @@ [core] -sections = ["Security", "Documentation", "Tests", "Features", "Internal", "BugFixes"] template = "default_template.rst" -start_string = "# Changelog\nAll notable changes to this project will be documented in this file." -title_format = "{name} {version} ({project_date})" -pr_format = "#{pr} " [types] feature = { name = "Features" } diff --git a/scripts/news/default_template.rst.jinja b/scripts/news/default_template.rst.jinja new file mode 100644 index 00000000..c5be05d2 --- /dev/null +++ b/scripts/news/default_template.rst.jinja @@ -0,0 +1,16 @@ +{% if top_line %} +# {{ top_line }} +{% elif version_data.name %} +# {{ version_data.name }} {{ version_data.version }} ({{ version_data.date }}) +{% else %} +# {{ version_data.version }} ({{ version_data.date }}) +{% endif %} + +--- + +{% for section, fragments in metadata.items() %} +### {{ section_names[section] }} +{% for fragment in fragments %} +- {{ fragment["news_entry"].strip("\n") }} (#{{ fragment["gh_pr"].split("-")[1] }}) +{% endfor %} +{% endfor %} diff --git a/scripts/news/utils.py b/scripts/news/utils.py index 21ad91b1..61caefc6 100644 --- a/scripts/news/utils.py +++ b/scripts/news/utils.py @@ -1,15 +1,17 @@ import base64 +import datetime import glob import hashlib import os import sys +import textwrap import traceback from pathlib import Path -from typing import Any, Dict, List, Mapping, Optional, Tuple +from typing import Any, Dict, List, Mapping, Optional, Tuple, Union -import click import tomli -from click import Option, echo, style +from click import Context, Option, echo, style +from jinja2 import Template from . import ERROR_MSG_PREFIX @@ -62,7 +64,7 @@ def __init__(self, *args, **kwargs): ).strip() super(NotRequiredIf, self).__init__(*args, **kwargs) - def handle_parse_result(self, ctx: click.Context, opts: Mapping[str, Any], args: List[str]): + def handle_parse_result(self, ctx: Context, opts: Mapping[str, Any], args: List[str]): we_are_present = self.name in opts other_present = self.not_required_if in opts @@ -113,7 +115,13 @@ def get_metadata_from_file(path: Path) -> dict: with open(path, "r", encoding="utf-8") as file: news_entry = file.read() - metadata = {"date": date, "gh_pr": gh_pr, "news_type": news_type, "nonce": nonce, "new_entry": news_entry} + metadata = { + "date": date, + "gh_pr": gh_pr, + "news_type": news_type, + "nonce": nonce, + "news_entry": news_entry, + } return metadata @@ -148,3 +156,41 @@ def load_toml_config() -> Dict[str, Any]: sys.exit(1) else: return toml_dict + + +def render_fragments( + section_names: Dict[str, dict], + template: Path, + metadata: Dict[str, list], + wrap: bool, + version_data: Tuple[str, str], + date: Union[str, datetime.datetime], +): + """Render the fragments into a news file.""" + print(template) + with open(template, mode="r") as template_file: + jinja_template = Template(template_file.read(), trim_blocks=True) + + version_data = {"name": version_data[0], "version": version_data[1], "date": date} + res = jinja_template.render( + section_names={_type: config["name"] for _type, config in section_names.items()}, + version_data=version_data, + metadata=metadata, + ) + + done = [] + for line in res.split("\n"): + if wrap: + done.append( + textwrap.fill( + line, + width=79, + subsequent_indent=" ", + break_long_words=False, + break_on_hyphens=False, + ) + ) + else: + done.append(line) + + return "\n".join(done).rstrip() + "\n" From 9dd44ffca23e181e3b1887f8f6168ced58f4f6d0 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sun, 17 Oct 2021 13:11:51 +0530 Subject: [PATCH 08/27] Clear scripts/news/next after generating version changelog --- scripts/news/__main__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index 963a6145..8fef315c 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -218,7 +218,7 @@ def cli_build_news(ctx: click.Context, version: str, force: bool, template: str) date = datetime.now(timezone.utc).strftime("%Y-%m-%d") if not filenames: - err(f"No news fragments found. Exiting") + err("No news fragments found. Exiting") ctx.exit() else: for filename in filenames: @@ -261,6 +261,12 @@ def cli_build_news(ctx: click.Context, version: str, force: bool, template: str) out(f"All done! ✨ 🍰 ✨ Created changelog at {changelog_path}") + files = Path(Path.cwd(), "scripts/news/next") + for news_fragment in files.glob("*.md"): + os.remove(news_fragment) + + out("🍰 Cleared existing `scripts/news/next` changelogs!") + if __name__ == "__main__": cli_main() From aa8bc6f0ec6749f94a759b6128c8525860344e56 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Mon, 18 Oct 2021 10:51:08 +0530 Subject: [PATCH 09/27] Add our project specific changelog config --- .gitignore | 2 +- scripts/news/{default_config.toml => config.toml} | 0 scripts/news/utils.py | 7 ------- 3 files changed, 1 insertion(+), 8 deletions(-) rename scripts/news/{default_config.toml => config.toml} (100%) diff --git a/.gitignore b/.gitignore index 5a267be8..d605b3bf 100644 --- a/.gitignore +++ b/.gitignore @@ -137,7 +137,7 @@ logs # Configuration *config.toml -!scripts/news/default_config.toml +!scripts/news/config.toml # Custom docker compose override docker-compose.override.yml diff --git a/scripts/news/default_config.toml b/scripts/news/config.toml similarity index 100% rename from scripts/news/default_config.toml rename to scripts/news/config.toml diff --git a/scripts/news/utils.py b/scripts/news/utils.py index 61caefc6..a2ff36dc 100644 --- a/scripts/news/utils.py +++ b/scripts/news/utils.py @@ -136,14 +136,7 @@ def get_project_meta() -> Tuple[str, str]: def load_toml_config() -> Dict[str, Any]: config_path = Path(Path.cwd(), "scripts/news/config.toml") - default_config_url = "URL HERE PLEASE" - if not config_path.exists(): - err( - f"Configuration not found. Create a config file at '{config_path}', and see " - f"'{default_config_url}' for an example configuration. " - ) - sys.exit(1) try: with open(config_path, mode="r") as file: toml_dict = tomli.loads(file.read()) From b6c19d4c8fae414c5ea3211f1f542eb1293b0669 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Mon, 18 Oct 2021 11:45:27 +0530 Subject: [PATCH 10/27] WIP: Add module for checking news file in a specific pull request --- poetry.lock | 137 +++++++++++++++++- pyproject.toml | 2 + requirements.txt | 3 + .../news/__pycache__/__init__.cpython-39.pyc | Bin 237 -> 237 bytes .../news/__pycache__/__main__.cpython-39.pyc | Bin 7434 -> 7809 bytes scripts/news/__pycache__/utils.cpython-39.pyc | Bin 6456 -> 6251 bytes scripts/news/check_news_workflow/__init__.py | 0 scripts/news/check_news_workflow/__main__.py | 83 +++++++++++ scripts/news/config.toml | 1 + 9 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 scripts/news/check_news_workflow/__init__.py create mode 100644 scripts/news/check_news_workflow/__main__.py diff --git a/poetry.lock b/poetry.lock index b4eaa0ae..285e3f12 100644 --- a/poetry.lock +++ b/poetry.lock @@ -236,6 +236,17 @@ humanfriendly = ">=9.1" [package.extras] cron = ["capturer (>=2.4)"] +[[package]] +name = "commonmark" +version = "0.9.1" +description = "Python parser for the CommonMark Markdown spec" +category = "main" +optional = false +python-versions = "*" + +[package.extras] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] + [[package]] name = "coverage" version = "5.5" @@ -250,6 +261,25 @@ toml = {version = "*", optional = true, markers = "extra == \"toml\""} [package.extras] toml = ["toml"] +[[package]] +name = "cryptography" +version = "35.0.0" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] +docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] +sdist = ["setuptools_rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] + [[package]] name = "discord.py" version = "2.0.0a0" @@ -436,6 +466,26 @@ python-dateutil = ">=2.8.1" [package.extras] dev = ["twine", "markdown", "flake8", "wheel"] +[[package]] +name = "gidgethub" +version = "5.0.1" +description = "An async GitHub API library" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +PyJWT = {version = ">=2.0.0", extras = ["crypto"]} +uritemplate = ">=3.0.1" + +[package.extras] +aiohttp = ["aiohttp"] +dev = ["aiohttp", "black", "coverage[toml] (>=5.0.3)", "httpx", "mypy", "pytest-cov", "pytest-xdist", "tornado"] +doc = ["sphinx"] +httpx = ["httpx (>=0.16.1)"] +test = ["importlib-resources", "pytest (>=5.4.1)", "pytest-asyncio", "pytest-tornasync"] +tornado = ["tornado"] + [[package]] name = "gitdb" version = "4.0.7" @@ -839,10 +889,27 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" name = "pygments" version = "2.10.0" description = "Pygments is a syntax highlighting package written in Python." -category = "dev" +category = "main" optional = false python-versions = ">=3.5" +[[package]] +name = "pyjwt" +version = "2.3.0" +description = "JSON Web Token implementation in Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cryptography = {version = ">=3.3.1", optional = true, markers = "extra == \"crypto\""} + +[package.extras] +crypto = ["cryptography (>=3.3.1)"] +dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"] +docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] + [[package]] name = "pymdown-extensions" version = "8.2" @@ -1043,6 +1110,22 @@ urllib3 = ">=1.21.1,<1.27" socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] +[[package]] +name = "rich" +version = "10.12.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +category = "main" +optional = false +python-versions = ">=3.6.2,<4.0.0" + +[package.dependencies] +colorama = ">=0.4.0,<0.5.0" +commonmark = ">=0.9.0,<0.10.0" +pygments = ">=2.6.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] + [[package]] name = "six" version = "1.16.0" @@ -1137,6 +1220,14 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "uritemplate" +version = "4.1.1" +description = "Implementation of RFC 6570 URI Templates" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "urllib3" version = "1.26.6" @@ -1207,7 +1298,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "e99a8dc25d0c2d30dd939af8e60aa92c8e4851614508fb46387d0663a0a8c261" +content-hash = "45e2d2658fd40f79150eb9bff33090f0dabda45b07905b513856dd7ca0656c32" [metadata.files] aiodns = [ @@ -1440,6 +1531,10 @@ coloredlogs = [ {file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"}, {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, ] +commonmark = [ + {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, + {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, +] coverage = [ {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, @@ -1494,6 +1589,28 @@ coverage = [ {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, ] +cryptography = [ + {file = "cryptography-35.0.0-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:d57e0cdc1b44b6cdf8af1d01807db06886f10177469312fbde8f44ccbb284bc9"}, + {file = "cryptography-35.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:ced40344e811d6abba00295ced98c01aecf0c2de39481792d87af4fa58b7b4d6"}, + {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:54b2605e5475944e2213258e0ab8696f4f357a31371e538ef21e8d61c843c28d"}, + {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7b7ceeff114c31f285528ba8b390d3e9cfa2da17b56f11d366769a807f17cbaa"}, + {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d69645f535f4b2c722cfb07a8eab916265545b3475fdb34e0be2f4ee8b0b15e"}, + {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2d0e0acc20ede0f06ef7aa58546eee96d2592c00f450c9acb89c5879b61992"}, + {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:07bb7fbfb5de0980590ddfc7f13081520def06dc9ed214000ad4372fb4e3c7f6"}, + {file = "cryptography-35.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7eba2cebca600a7806b893cb1d541a6e910afa87e97acf2021a22b32da1df52d"}, + {file = "cryptography-35.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:18d90f4711bf63e2fb21e8c8e51ed8189438e6b35a6d996201ebd98a26abbbe6"}, + {file = "cryptography-35.0.0-cp36-abi3-win32.whl", hash = "sha256:c10c797ac89c746e488d2ee92bd4abd593615694ee17b2500578b63cad6b93a8"}, + {file = "cryptography-35.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:7075b304cd567694dc692ffc9747f3e9cb393cc4aa4fb7b9f3abd6f5c4e43588"}, + {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a688ebcd08250eab5bb5bca318cc05a8c66de5e4171a65ca51db6bd753ff8953"}, + {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99915d6ab265c22873f1b4d6ea5ef462ef797b4140be4c9d8b179915e0985c6"}, + {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:928185a6d1ccdb816e883f56ebe92e975a262d31cc536429041921f8cb5a62fd"}, + {file = "cryptography-35.0.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ebeddd119f526bcf323a89f853afb12e225902a24d29b55fe18dd6fcb2838a76"}, + {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22a38e96118a4ce3b97509443feace1d1011d0571fae81fc3ad35f25ba3ea999"}, + {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb80e8a1f91e4b7ef8b33041591e6d89b2b8e122d787e87eeb2b08da71bb16ad"}, + {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:abb5a361d2585bb95012a19ed9b2c8f412c5d723a9836418fab7aaa0243e67d2"}, + {file = "cryptography-35.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1ed82abf16df40a60942a8c211251ae72858b25b7421ce2497c2eb7a1cee817c"}, + {file = "cryptography-35.0.0.tar.gz", hash = "sha256:9933f28f70d0517686bd7de36166dda42094eac49415459d9bdf5e7df3e0086d"}, +] "discord.py" = [] distlib = [ {file = "distlib-0.3.2-py2.py3-none-any.whl", hash = "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c"}, @@ -1549,6 +1666,10 @@ ghp-import = [ {file = "ghp-import-2.0.1.tar.gz", hash = "sha256:753de2eace6e0f7d4edfb3cce5e3c3b98cd52aadb80163303d1d036bda7b4483"}, {file = "ghp_import-2.0.1-py3-none-any.whl", hash = "sha256:8241a8e9f8dd3c1fafe9696e6e081b57a208ef907e9939c44e7415e407ab40ea"}, ] +gidgethub = [ + {file = "gidgethub-5.0.1-py3-none-any.whl", hash = "sha256:67245e93eb0918b37df038148af675df43b62e832c529d7f859f6b90d9f3e70d"}, + {file = "gidgethub-5.0.1.tar.gz", hash = "sha256:3efbd6998600254ec7a2869318bd3ffde38edc3a0d37be0c14bc46b45947b682"}, +] gitdb = [ {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"}, {file = "gitdb-4.0.7.tar.gz", hash = "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"}, @@ -1861,6 +1982,10 @@ pygments = [ {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, ] +pyjwt = [ + {file = "PyJWT-2.3.0-py3-none-any.whl", hash = "sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f"}, + {file = "PyJWT-2.3.0.tar.gz", hash = "sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41"}, +] pymdown-extensions = [ {file = "pymdown-extensions-8.2.tar.gz", hash = "sha256:b6daa94aad9e1310f9c64c8b1f01e4ce82937ab7eb53bfc92876a97aca02a6f4"}, {file = "pymdown_extensions-8.2-py3-none-any.whl", hash = "sha256:141452d8ed61165518f2c923454bf054866b85cf466feedb0eb68f04acdc2560"}, @@ -1990,6 +2115,10 @@ requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, ] +rich = [ + {file = "rich-10.12.0-py3-none-any.whl", hash = "sha256:c30d6808d1cd3defd56a7bd2d587d13e53b5f55de6cf587f035bcbb56bc3f37b"}, + {file = "rich-10.12.0.tar.gz", hash = "sha256:83fb3eff778beec3c55201455c17cccde1ccdf66d5b4dade8ef28f56b50c4bd4"}, +] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -2030,6 +2159,10 @@ typing-extensions = [ {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, ] +uritemplate = [ + {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, + {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, +] urllib3 = [ {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, diff --git a/pyproject.toml b/pyproject.toml index aa3423f0..575f2254 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ coloredlogs = "^15.0" "discord.py" = { url = "https://github.com/Rapptz/discord.py/archive/master.zip" } pydantic = { version = "^1.8.2", extras = ["dotenv"] } toml = "^0.10.2" +rich = "^10.12.0" [tool.poetry.extras] @@ -58,6 +59,7 @@ mkdocs-material = ">=7.1.9,<8.0.0" mkdocs-markdownextradata-plugin = ">=0.1.7,<0.2.0" click = "^8.0.3" Jinja2 = "^3.0.2" +gidgethub = "^5.0.1" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/requirements.txt b/requirements.txt index 701d5a63..dfe57f98 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,7 @@ cffi==1.14.6 chardet==4.0.0 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" colorama==0.4.4 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" coloredlogs==15.0.1 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" +commonmark==0.9.1 discord.py @ https://github.com/Rapptz/discord.py/archive/master.zip ; python_full_version >= "3.8.0" humanfriendly==9.2 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" idna==3.2 ; python_version >= "3.5" @@ -19,9 +20,11 @@ multidict==5.1.0 ; python_version >= "3.6" pycares==4.0.0 pycparser==2.20 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" pydantic==1.8.2 ; python_full_version >= "3.6.1" +pygments==2.10.0 ; python_version >= "3.5" pyreadline==2.1 ; sys_platform == "win32" python-dateutil==2.8.2 ; python_version != "3.0" python-dotenv==0.19.0 ; python_version >= "3.5" +rich==10.12.0 ; python_full_version >= "3.6.2" six==1.16.0 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" toml==0.10.2 ; python_version >= "2.6" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" typing-extensions==3.10.0.2 diff --git a/scripts/news/__pycache__/__init__.cpython-39.pyc b/scripts/news/__pycache__/__init__.cpython-39.pyc index 7a93148275d334188e5cd96e99a22729fc58c0fd..c534847555f6b95d910612e7e2e32158799b0943 100644 GIT binary patch delta 19 ZcmaFM_?D47k(ZZ?0SLBB?!DV{t5-8r zwL)Ou`uhpv6F*k3%Q+yRs=)E*IJhdZ2D9G2<{g@1YU^XFD1X#niki z4~&VP&#azTvdrzTpYAd4oGGtJ%t9kkPcs8%a9(^6%lbWkzRpt%Omt8%Yh*zdauN{i zV5hJE+<1oow&Dsy5;H@dwlydpVOu=RG^@Spe7zYdfD^(sJ*$(3e50px%>^*Ez<9N6 zXdjcmAhbkjSJVTT7Nh%&jOvc!4UaI;Fy!PP1v)i z4W2P35OmWR!5dB*!%2gT!=wu_VvOW8g2aLGor3Wu`Y9S^UYpU~O4*hLJyh2_m{-lD|e=(u4gkmV%Yrj}h;qv^V- zOGlUj<`-Y8+@5QgI9TQ7Acx}|`Z(|>G9)J~X9OQ($C83k%9%zj0lbU<9^^2^0n5|KEgo$D zq4*=B23kdpAqx@b=$F__-Qz9;95Nhc=z9HB#A3&iJmNSO&hhF=4q@gPq5}=hK&8_S Ji&9So_zev1`(FS6 delta 557 zcmX|-O=uHA6vyYyuG=)pZZ|QTHedCiSYoQFih@c->qRe8QK-g4*k+fsCfOuAqaxiX z5(N>8mUjuL(Be(R5B4Hn1hF>}yh#=G>Os7UUYzNn^LRi0hu^#z-tFo8M-5xoqePsa ztYHtvSB%e)di`S;#k4Y$+%W4duXgD z!S?EIvt6$kTd~@)`2v2=?a*Ecl^sVlU(6bmf(CT6RG@#gh(Qh5L;ah0Ise{Vl0(l) g+>p42dkfuoL&ObrzTIvL>set26#P|KQA$zx4@>lqzW@LL diff --git a/scripts/news/__pycache__/utils.cpython-39.pyc b/scripts/news/__pycache__/utils.cpython-39.pyc index 1246dd9432f98fb034bbec6a5dd0540949edf733..af48b881982e3a25bd55a42294b54a74db65bf0b 100644 GIT binary patch delta 604 zcmXX?&ubGw6rMLbyV-2Ao6R=A@K6M`kV~4X7ezEu5kc@$Y)GmpAzL-2ZsP7hZQQNV z^x~l?4D{f|T`yMfB01E*z@vB$-h1#bum$mL;(WYszM1#U%zN|g;_s@_)^&~GI4U{q zTJfuK7a%X1aI7Yldk)`t&P9#p$1Qq76&-DEdfbvvWzfdW)P$WW#7C(D7Byi(qVON5 zN@9G=Gy#Ugft!lg}R=Od5+J%ejD3ln&s9I8ASYCN^(OC#{5h(-|6 zfbq1?MgWBCphW#Qxd-#^h3V4U@!T76UT&sl2iN;;{zUwmyK?Rg z?t3L90tTgEKov}2_=~x}jHz&zxes@mBN{9(NDK&r&^W6_N!@!@_5gKKTa>fr@+p delta 791 zcmZ8e%}*0S6rVS{-F}zSFE9qP2?{nbrJ#w?7!^qq6A2oKG*pFbx?8D~-MTvsP`47S z+%U$>fdetwy_tCMqX+#fIGc@k4*mytz_*l>GkL#x^Ly|8X6DVG;?Jps5|2j+juUH5 zxi|bR@cWN98B20{P+x-^$UUFbigGlbZLjEv;#JAs7tJyOi%@z z91vBs0Wt6~&;=k&bONg6qSwC7B%7!~HS~_YztEv9=!9(Qgk9o7M<^4Oh&a0f5Is3b ztVWRl*#?9qv@+2bMZ$tFB3p+*IwH#5Qo~LZ1tU(3#hnBju*ELeqDx(HX(zx0`@aj6 zQAx$x*P@>2uSHTMwy=Qg0?9|`+QU}WtT&l8p=%q~MAgu@YxU;^W~?{b1CQrt<@=@i zlKg14G`&!2Phndz+A9MkI+~X%(Z0$`Q(>!>l&aN~R)ejaku9^q^XVHST`aIJBM?Y{NJBsY?L;~e#RzaC zorN8~n_h%N{yY67#E4D&&*axLSK%vP%bbtz* bool: + """Return True if file is in the News directory.""" + return filename.startswith(NEWS_NEXT_DIR) + + +@click.command() +@click.argument("pr", nargs=1, type=int) +def main(pr: int) -> None: + """Main function to check for a changelog entry.""" + r = requests.get(f"{GH_API_URL}repos/discord-modmail/modmail/pulls/{pr}/files", headers=HEADERS) + files_changed = r.json() + in_next_dir = file_found = False + status = None + + for file in files_changed: + if not is_news_dir(file["filename"]): + continue + in_next_dir = True + file_path = pathlib.PurePath(file["filename"]) + if len(file_path.parts) != 4: # news, next, , + continue + file_found = True + if FILENAME_RE.match(file_path.name) and len(file["patch"]) >= 1: + status = (f"News entry found in {NEWS_NEXT_DIR}", StatusState.SUCCESS) + break + else: + _r = requests.get(f"{GH_API_URL}repos/discord-modmail/modmail/pulls/{pr}", headers=HEADERS) + pr_data = _r.json() + labels = [label["name"] for label in pr_data["labels"]] + if SKIP_NEWS_LABEL in labels: + description = f"'{SKIP_NEWS_LABEL}' label found" + else: + if not in_next_dir: + description = f'No news entry in {NEWS_NEXT_DIR} or "{SKIP_NEWS_LABEL}" label found' + elif not file_found: + description = "News entry not in an appropriate directory" + else: + description = "News entry file name incorrectly formatted" + + status = (description, StatusState.ERROR) + + print(status) + + +if __name__ == "__main__": + main() diff --git a/scripts/news/config.toml b/scripts/news/config.toml index c300c997..a77b70de 100644 --- a/scripts/news/config.toml +++ b/scripts/news/config.toml @@ -9,3 +9,4 @@ bugfix = { name = "Bug Fixes" } doc = { name = "Improved Documentation" } deprecation = { name = "Deprecations" } breaking = { name = "Breaking Changes" } +internal = { name = "Internal" } From 87ffb04f9ec305f4a1f5ec1aa283a832f2639a6d Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 19 Oct 2021 06:52:50 +0530 Subject: [PATCH 11/27] Relock deps; remove redundant ones --- poetry.lock | 137 +------------------ pyproject.toml | 2 - requirements.txt | 3 - scripts/news/check_news_workflow/__main__.py | 1 - 4 files changed, 2 insertions(+), 141 deletions(-) diff --git a/poetry.lock b/poetry.lock index 285e3f12..b4eaa0ae 100644 --- a/poetry.lock +++ b/poetry.lock @@ -236,17 +236,6 @@ humanfriendly = ">=9.1" [package.extras] cron = ["capturer (>=2.4)"] -[[package]] -name = "commonmark" -version = "0.9.1" -description = "Python parser for the CommonMark Markdown spec" -category = "main" -optional = false -python-versions = "*" - -[package.extras] -test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] - [[package]] name = "coverage" version = "5.5" @@ -261,25 +250,6 @@ toml = {version = "*", optional = true, markers = "extra == \"toml\""} [package.extras] toml = ["toml"] -[[package]] -name = "cryptography" -version = "35.0.0" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cffi = ">=1.12" - -[package.extras] -docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] -docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] -pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] -sdist = ["setuptools_rust (>=0.11.4)"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] - [[package]] name = "discord.py" version = "2.0.0a0" @@ -466,26 +436,6 @@ python-dateutil = ">=2.8.1" [package.extras] dev = ["twine", "markdown", "flake8", "wheel"] -[[package]] -name = "gidgethub" -version = "5.0.1" -description = "An async GitHub API library" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -PyJWT = {version = ">=2.0.0", extras = ["crypto"]} -uritemplate = ">=3.0.1" - -[package.extras] -aiohttp = ["aiohttp"] -dev = ["aiohttp", "black", "coverage[toml] (>=5.0.3)", "httpx", "mypy", "pytest-cov", "pytest-xdist", "tornado"] -doc = ["sphinx"] -httpx = ["httpx (>=0.16.1)"] -test = ["importlib-resources", "pytest (>=5.4.1)", "pytest-asyncio", "pytest-tornasync"] -tornado = ["tornado"] - [[package]] name = "gitdb" version = "4.0.7" @@ -889,26 +839,9 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" name = "pygments" version = "2.10.0" description = "Pygments is a syntax highlighting package written in Python." -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "pyjwt" -version = "2.3.0" -description = "JSON Web Token implementation in Python" category = "dev" optional = false -python-versions = ">=3.6" - -[package.dependencies] -cryptography = {version = ">=3.3.1", optional = true, markers = "extra == \"crypto\""} - -[package.extras] -crypto = ["cryptography (>=3.3.1)"] -dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"] -docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] -tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] +python-versions = ">=3.5" [[package]] name = "pymdown-extensions" @@ -1110,22 +1043,6 @@ urllib3 = ">=1.21.1,<1.27" socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] -[[package]] -name = "rich" -version = "10.12.0" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -category = "main" -optional = false -python-versions = ">=3.6.2,<4.0.0" - -[package.dependencies] -colorama = ">=0.4.0,<0.5.0" -commonmark = ">=0.9.0,<0.10.0" -pygments = ">=2.6.0,<3.0.0" - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] - [[package]] name = "six" version = "1.16.0" @@ -1220,14 +1137,6 @@ category = "main" optional = false python-versions = "*" -[[package]] -name = "uritemplate" -version = "4.1.1" -description = "Implementation of RFC 6570 URI Templates" -category = "dev" -optional = false -python-versions = ">=3.6" - [[package]] name = "urllib3" version = "1.26.6" @@ -1298,7 +1207,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "45e2d2658fd40f79150eb9bff33090f0dabda45b07905b513856dd7ca0656c32" +content-hash = "e99a8dc25d0c2d30dd939af8e60aa92c8e4851614508fb46387d0663a0a8c261" [metadata.files] aiodns = [ @@ -1531,10 +1440,6 @@ coloredlogs = [ {file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"}, {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, ] -commonmark = [ - {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, - {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, -] coverage = [ {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, @@ -1589,28 +1494,6 @@ coverage = [ {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, ] -cryptography = [ - {file = "cryptography-35.0.0-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:d57e0cdc1b44b6cdf8af1d01807db06886f10177469312fbde8f44ccbb284bc9"}, - {file = "cryptography-35.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:ced40344e811d6abba00295ced98c01aecf0c2de39481792d87af4fa58b7b4d6"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:54b2605e5475944e2213258e0ab8696f4f357a31371e538ef21e8d61c843c28d"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7b7ceeff114c31f285528ba8b390d3e9cfa2da17b56f11d366769a807f17cbaa"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d69645f535f4b2c722cfb07a8eab916265545b3475fdb34e0be2f4ee8b0b15e"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2d0e0acc20ede0f06ef7aa58546eee96d2592c00f450c9acb89c5879b61992"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:07bb7fbfb5de0980590ddfc7f13081520def06dc9ed214000ad4372fb4e3c7f6"}, - {file = "cryptography-35.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7eba2cebca600a7806b893cb1d541a6e910afa87e97acf2021a22b32da1df52d"}, - {file = "cryptography-35.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:18d90f4711bf63e2fb21e8c8e51ed8189438e6b35a6d996201ebd98a26abbbe6"}, - {file = "cryptography-35.0.0-cp36-abi3-win32.whl", hash = "sha256:c10c797ac89c746e488d2ee92bd4abd593615694ee17b2500578b63cad6b93a8"}, - {file = "cryptography-35.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:7075b304cd567694dc692ffc9747f3e9cb393cc4aa4fb7b9f3abd6f5c4e43588"}, - {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a688ebcd08250eab5bb5bca318cc05a8c66de5e4171a65ca51db6bd753ff8953"}, - {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99915d6ab265c22873f1b4d6ea5ef462ef797b4140be4c9d8b179915e0985c6"}, - {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:928185a6d1ccdb816e883f56ebe92e975a262d31cc536429041921f8cb5a62fd"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ebeddd119f526bcf323a89f853afb12e225902a24d29b55fe18dd6fcb2838a76"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22a38e96118a4ce3b97509443feace1d1011d0571fae81fc3ad35f25ba3ea999"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb80e8a1f91e4b7ef8b33041591e6d89b2b8e122d787e87eeb2b08da71bb16ad"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:abb5a361d2585bb95012a19ed9b2c8f412c5d723a9836418fab7aaa0243e67d2"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1ed82abf16df40a60942a8c211251ae72858b25b7421ce2497c2eb7a1cee817c"}, - {file = "cryptography-35.0.0.tar.gz", hash = "sha256:9933f28f70d0517686bd7de36166dda42094eac49415459d9bdf5e7df3e0086d"}, -] "discord.py" = [] distlib = [ {file = "distlib-0.3.2-py2.py3-none-any.whl", hash = "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c"}, @@ -1666,10 +1549,6 @@ ghp-import = [ {file = "ghp-import-2.0.1.tar.gz", hash = "sha256:753de2eace6e0f7d4edfb3cce5e3c3b98cd52aadb80163303d1d036bda7b4483"}, {file = "ghp_import-2.0.1-py3-none-any.whl", hash = "sha256:8241a8e9f8dd3c1fafe9696e6e081b57a208ef907e9939c44e7415e407ab40ea"}, ] -gidgethub = [ - {file = "gidgethub-5.0.1-py3-none-any.whl", hash = "sha256:67245e93eb0918b37df038148af675df43b62e832c529d7f859f6b90d9f3e70d"}, - {file = "gidgethub-5.0.1.tar.gz", hash = "sha256:3efbd6998600254ec7a2869318bd3ffde38edc3a0d37be0c14bc46b45947b682"}, -] gitdb = [ {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"}, {file = "gitdb-4.0.7.tar.gz", hash = "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"}, @@ -1982,10 +1861,6 @@ pygments = [ {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, ] -pyjwt = [ - {file = "PyJWT-2.3.0-py3-none-any.whl", hash = "sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f"}, - {file = "PyJWT-2.3.0.tar.gz", hash = "sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41"}, -] pymdown-extensions = [ {file = "pymdown-extensions-8.2.tar.gz", hash = "sha256:b6daa94aad9e1310f9c64c8b1f01e4ce82937ab7eb53bfc92876a97aca02a6f4"}, {file = "pymdown_extensions-8.2-py3-none-any.whl", hash = "sha256:141452d8ed61165518f2c923454bf054866b85cf466feedb0eb68f04acdc2560"}, @@ -2115,10 +1990,6 @@ requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, ] -rich = [ - {file = "rich-10.12.0-py3-none-any.whl", hash = "sha256:c30d6808d1cd3defd56a7bd2d587d13e53b5f55de6cf587f035bcbb56bc3f37b"}, - {file = "rich-10.12.0.tar.gz", hash = "sha256:83fb3eff778beec3c55201455c17cccde1ccdf66d5b4dade8ef28f56b50c4bd4"}, -] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -2159,10 +2030,6 @@ typing-extensions = [ {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, ] -uritemplate = [ - {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, - {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, -] urllib3 = [ {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, diff --git a/pyproject.toml b/pyproject.toml index 575f2254..aa3423f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,6 @@ coloredlogs = "^15.0" "discord.py" = { url = "https://github.com/Rapptz/discord.py/archive/master.zip" } pydantic = { version = "^1.8.2", extras = ["dotenv"] } toml = "^0.10.2" -rich = "^10.12.0" [tool.poetry.extras] @@ -59,7 +58,6 @@ mkdocs-material = ">=7.1.9,<8.0.0" mkdocs-markdownextradata-plugin = ">=0.1.7,<0.2.0" click = "^8.0.3" Jinja2 = "^3.0.2" -gidgethub = "^5.0.1" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/requirements.txt b/requirements.txt index dfe57f98..701d5a63 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,6 @@ cffi==1.14.6 chardet==4.0.0 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" colorama==0.4.4 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" coloredlogs==15.0.1 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" -commonmark==0.9.1 discord.py @ https://github.com/Rapptz/discord.py/archive/master.zip ; python_full_version >= "3.8.0" humanfriendly==9.2 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" idna==3.2 ; python_version >= "3.5" @@ -20,11 +19,9 @@ multidict==5.1.0 ; python_version >= "3.6" pycares==4.0.0 pycparser==2.20 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" pydantic==1.8.2 ; python_full_version >= "3.6.1" -pygments==2.10.0 ; python_version >= "3.5" pyreadline==2.1 ; sys_platform == "win32" python-dateutil==2.8.2 ; python_version != "3.0" python-dotenv==0.19.0 ; python_version >= "3.5" -rich==10.12.0 ; python_full_version >= "3.6.2" six==1.16.0 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" toml==0.10.2 ; python_version >= "2.6" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" typing-extensions==3.10.0.2 diff --git a/scripts/news/check_news_workflow/__main__.py b/scripts/news/check_news_workflow/__main__.py index efaf96e0..e1de1243 100644 --- a/scripts/news/check_news_workflow/__main__.py +++ b/scripts/news/check_news_workflow/__main__.py @@ -4,7 +4,6 @@ import click import requests -from rich import print from ..utils import load_toml_config From 6b2d5efb36d040338ca4ce2367c5276600e3a6cd Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 19 Oct 2021 16:12:58 +0530 Subject: [PATCH 12/27] Add support to run check news from workflow --- .github/workflows/changelog.yml | 27 +++++----- scripts/news/check_news_workflow/__main__.py | 52 +++++++++++--------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index f13b65d4..99a16aa9 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -1,6 +1,3 @@ -# Github Action Workflow enforcing our changelog style of enforcing PR number. -# credit: https://github.com/psf/black/blob/main/.github/workflows/changelog.yml - name: Changelog Entry Check on: @@ -8,16 +5,24 @@ on: types: [opened, synchronize, labeled, unlabeled, reopened] jobs: - build: - name: Changelog Entry Check + check-news: runs-on: ubuntu-latest steps: + - name: Checkout repository + uses: actions/checkout@v2 + - uses: actions/checkout@v2 - - name: Grep Release Notes (docs-en) for PR number - if: contains(github.event.pull_request.labels.*.name, 'skip changelog') != true + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.9' + + - name: Install dependencies using poetry + if: steps.python_cache.outputs.cache-hit != 'true' + run: | + pip install tomli requests + + - name: Changelog Entry Check for the current PR run: | - grep -Pz "\((\n\s*)?#${{ github.event.pull_request.number }}(\n\s*)?\)" docs/changelog.md || \ - ( - echo "Please add '(#${{ github.event.pull_request.number }})' change line to docs/changelog.md" && exit 1 - ) + python -m scripts.news.check_news_workflow ${{ github.event.pull_request.number }} diff --git a/scripts/news/check_news_workflow/__main__.py b/scripts/news/check_news_workflow/__main__.py index e1de1243..adea0ec3 100644 --- a/scripts/news/check_news_workflow/__main__.py +++ b/scripts/news/check_news_workflow/__main__.py @@ -1,18 +1,35 @@ -import enum import pathlib import re +import sys +import traceback +from typing import Tuple -import click import requests - -from ..utils import load_toml_config - +import tomli NEWS_NEXT_DIR = "news/next/" SKIP_NEWS_LABEL = "skip changelog" GH_API_URL = "https://api.github.com/" HEADERS = {"accept": "application/vnd.github.v3+json"} + +def load_toml_config() -> dict: + config_path = pathlib.Path(pathlib.Path.cwd(), "scripts/news/config.toml") + + try: + with open(config_path, mode="r") as file: + toml_dict = tomli.loads(file.read()) + except tomli.TOMLDecodeError as e: + message = "Invalid changelog news configuration at {}\n{}".format( + config_path, + "".join(traceback.format_exception_only(type(e), e)), + ) + print(message) + sys.exit(1) + else: + return toml_dict + + CONFIG = load_toml_config() SECTIONS = [_type for _type, _ in CONFIG.get("types").items()] @@ -21,32 +38,21 @@ r"pr-\d+(?:,\d+)*\." # Issue number(s) fr"({'|'.join(SECTIONS)})\." # Section type r"[A-Za-z0-9_=-]+\." # Nonce (URL-safe base64) - r"md", # File extension""" + r"md", # File extension re.VERBOSE, ) -class StatusState(enum.Enum): - """Status state for the changelog checking.""" - - SUCCESS = "success" - ERROR = "error" - FAILURE = "failure" - - def is_news_dir(filename: str) -> bool: """Return True if file is in the News directory.""" return filename.startswith(NEWS_NEXT_DIR) -@click.command() -@click.argument("pr", nargs=1, type=int) -def main(pr: int) -> None: +def main(pr: int) -> Tuple[str, bool]: """Main function to check for a changelog entry.""" r = requests.get(f"{GH_API_URL}repos/discord-modmail/modmail/pulls/{pr}/files", headers=HEADERS) files_changed = r.json() in_next_dir = file_found = False - status = None for file in files_changed: if not is_news_dir(file["filename"]): @@ -57,7 +63,7 @@ def main(pr: int) -> None: continue file_found = True if FILENAME_RE.match(file_path.name) and len(file["patch"]) >= 1: - status = (f"News entry found in {NEWS_NEXT_DIR}", StatusState.SUCCESS) + status = (f"News entry found in {NEWS_NEXT_DIR}", True) break else: _r = requests.get(f"{GH_API_URL}repos/discord-modmail/modmail/pulls/{pr}", headers=HEADERS) @@ -73,10 +79,12 @@ def main(pr: int) -> None: else: description = "News entry file name incorrectly formatted" - status = (description, StatusState.ERROR) + status = (description, False) - print(status) + return status if __name__ == "__main__": - main() + message, status = main(int(sys.argv[1])) + print(message) + sys.exit(0 if status else 1) From 4f45db1db17176d62b99475c896a0e8583f327c9 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Wed, 20 Oct 2021 05:42:02 +0530 Subject: [PATCH 13/27] Document and remove redundant code --- .github/workflows/{changelog.yml => news.yml} | 12 +- scripts/news/__main__.py | 122 +++++++----------- scripts/news/check_news_workflow/__main__.py | 14 +- scripts/news/utils.py | 20 ++- tox.ini | 1 + 5 files changed, 74 insertions(+), 95 deletions(-) rename .github/workflows/{changelog.yml => news.yml} (51%) diff --git a/.github/workflows/changelog.yml b/.github/workflows/news.yml similarity index 51% rename from .github/workflows/changelog.yml rename to .github/workflows/news.yml index 99a16aa9..8376dc60 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/news.yml @@ -1,4 +1,4 @@ -name: Changelog Entry Check +name: Check for a news entry on: pull_request: @@ -9,20 +9,24 @@ jobs: runs-on: ubuntu-latest steps: + # Checks out the repository in the current folder. - name: Checkout repository uses: actions/checkout@v2 + # Set up the right version of Python - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.9' - - name: Install dependencies using poetry - if: steps.python_cache.outputs.cache-hit != 'true' + # Install the required dependencies for using the feature, we aren't using cache here + # as the number is relatively small and having cache would be complete redundancy. + - name: Install dependencies using pip run: | pip install tomli requests - - name: Changelog Entry Check for the current PR + # Run the script for checking if there is a news entry for the current PR + - name: Check for a news entry in the news/next run: | python -m scripts.news.check_news_workflow ${{ github.event.pull_request.number }} diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index 8fef315c..4c0e9971 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -45,51 +45,44 @@ SECTIONS = [_type for _type, _ in CONFIG.get("types").items()] -class NewsFragment: - def __init__(self, ctx: click.Context, gh_pr: int, nonce: str, news_entry: str, _type: str) -> None: - self.ctx = ctx - self.gh_pr = gh_pr - self.nonce = nonce - self.news_entry = news_entry - self.news_type = _type - self.date = datetime.now(timezone.utc).strftime("%Y-%m-%d") - - def save_file(self) -> None: - """Save received changelog data to a news file.""" - path = Path( - Path.cwd(), - f"news/next/{self.news_type}/{self.date}.pr-{self.gh_pr}.{self.news_type}.{self.nonce}.md", - ) - if not path.parents[1].exists(): - err(NO_NEWS_PATH_ERROR, fg="blue") - self.ctx.exit(1) - elif not path.parents[0].exists(): - make_news_type_dir = click.confirm( - f"Should I make the new type DIR for the news type at {path.parents[0]}" - ) - if make_news_type_dir: - path.parents[0].mkdir(exist_ok=True) - elif path.exists(): - # The file exists - err(f"{ERROR_MSG_PREFIX} {Path(os.path.relpath(path, start=Path.cwd()))} already exists") - self.ctx.exit(1) - - text = str(self.news_entry) - with open(path, "wt", encoding="utf-8") as file: - file.write(text) - - # Add news fragment to git stage - subprocess.run(["git", "add", "--force", path]).check_returncode() - - out( - f"All done! ✨ 🍰 ✨ Created news fragment at {Path(os.path.relpath(path, start=Path.cwd()))}" - "\nYou are now ready for commit!" +def save_news_fragment(ctx: click.Context, gh_pr: int, nonce: str, news_entry: str, news_type: str) -> None: + """Save received changelog data to a news file.""" + date = datetime.now(timezone.utc).strftime("%Y-%m-%d") + path = Path( + Path.cwd(), + f"news/next/{news_type}/{date}.pr-{str(gh_pr)}.{news_type}.{nonce}.md", + ) + if not path.parents[1].exists(): + err(NO_NEWS_PATH_ERROR, fg="blue") + ctx.exit(1) + elif not path.parents[0].exists(): + make_news_type_dir = click.confirm( + f"Should I make the new type DIR for the news type at {path.parents[0]}" ) + if make_news_type_dir: + path.parents[0].mkdir(exist_ok=True) + elif path.exists(): + # The file exists + err(f"{ERROR_MSG_PREFIX} {Path(os.path.relpath(path, start=Path.cwd()))} already exists") + ctx.exit(1) + + text = str(news_entry) + with open(path, "wt", encoding="utf-8") as file: + file.write(text) + + # Add news fragment to git stage + subprocess.run(["git", "add", "--force", path]).check_returncode() + + out( + f"All done! ✨ 🍰 ✨ Created news fragment at {Path(os.path.relpath(path, start=Path.cwd()))}" + "\nYou are now ready for commit!" + ) def validate_pull_request_number( - ctx: click.Context, param: click.Parameter, value: Optional[int] + ctx: click.Context, _param: click.Parameter, value: Optional[int] ) -> Optional[int]: + """Check if the given pull request number exists on the github repository.""" r = requests.get(PR_ENDPOINT.format(number=value)) if r.status_code == 403: if r.headers.get("X-RateLimit-Remaining") == "0": @@ -109,8 +102,7 @@ def validate_pull_request_number( @click.group(context_settings=dict(help_option_names=["-h", "--help"]), invoke_without_command=True) @click.version_option(version=__version__) -@click.pass_context -def cli_main(ctx: click.Context) -> None: +def cli_main() -> None: """ Modmail News 📜🤖. @@ -150,7 +142,7 @@ def cli_main(ctx: click.Context) -> None: ) @click.pass_context def cli_add_news(ctx: click.Context, message: str, editor: str, type: str, pr_number: int) -> None: - """Add a changelog 📜 (news/ entry) to the current discord-modmail repo for your awesome change!""" + """Add a news entry 📜 to the current discord-modmail repo for your awesome change!""" if not message: message_notes = [] while True: @@ -174,43 +166,17 @@ def cli_add_news(ctx: click.Context, message: str, editor: str, type: str, pr_nu ) if message is None: - out("Aborting creating new news fragment/changelog!", fg="yellow") + out("Aborting creating a new news fragment!", fg="yellow") ctx.exit(1) break - news_fragment = NewsFragment(ctx, pr_number, nonceify(message), message, type) - news_fragment.save_file() + save_news_fragment(ctx, pr_number, nonceify(message), message, type) @cli_main.command("build") -@click.option( - "--version", - type=str, -) -@click.option( - "-f", - "--force", - is_flag=True, - default=False, - help="Do not ask for confirmation to remove news fragments.", -) -@click.option( - "-t", - "--template", - type=click.Path( - exists=True, - file_okay=True, - dir_okay=False, - readable=True, - allow_dash=False, - path_type=str, - ), - is_eager=True, - help="Read JINJA2 template from FILE path.", -) @click.pass_context -def cli_build_news(ctx: click.Context, version: str, force: bool, template: str) -> None: +def cli_build_news(ctx: click.Context) -> None: """Build a combined news file 📜 from news fragments.""" filenames = glob_fragments("next", SECTIONS) _file_metadata = {} @@ -246,7 +212,7 @@ def cli_build_news(ctx: click.Context, version: str, force: bool, template: str) ) name, version = get_project_meta() - changelog = render_fragments( + version_news = render_fragments( section_names=CONFIG["types"], template=template, metadata=file_metadata, @@ -254,18 +220,18 @@ def cli_build_news(ctx: click.Context, version: str, force: bool, template: str) version_data=(name, version), date=date, ) - changelog_path = Path(Path.cwd(), f"news/{version}.md") + news_path = Path(Path.cwd(), f"news/{version}.md") - with open(changelog_path, mode="w") as filename: - filename.write(changelog) + with open(news_path, mode="w") as filename: + filename.write(version_news) - out(f"All done! ✨ 🍰 ✨ Created changelog at {changelog_path}") + out(f"All done! ✨ 🍰 ✨ Created {name}-v{version} news at {news_path}") files = Path(Path.cwd(), "scripts/news/next") for news_fragment in files.glob("*.md"): os.remove(news_fragment) - out("🍰 Cleared existing `scripts/news/next` changelogs!") + out("🍰 Cleared existing `scripts/news/next` news fragments!") if __name__ == "__main__": diff --git a/scripts/news/check_news_workflow/__main__.py b/scripts/news/check_news_workflow/__main__.py index adea0ec3..718344eb 100644 --- a/scripts/news/check_news_workflow/__main__.py +++ b/scripts/news/check_news_workflow/__main__.py @@ -7,6 +7,7 @@ import requests import tomli + NEWS_NEXT_DIR = "news/next/" SKIP_NEWS_LABEL = "skip changelog" GH_API_URL = "https://api.github.com/" @@ -14,13 +15,14 @@ def load_toml_config() -> dict: + """Load the news TOML configuration file and exit if found to be invalid.""" config_path = pathlib.Path(pathlib.Path.cwd(), "scripts/news/config.toml") try: with open(config_path, mode="r") as file: toml_dict = tomli.loads(file.read()) except tomli.TOMLDecodeError as e: - message = "Invalid changelog news configuration at {}\n{}".format( + message = "Invalid news configuration at {0}\n{1}".format( config_path, "".join(traceback.format_exception_only(type(e), e)), ) @@ -70,16 +72,14 @@ def main(pr: int) -> Tuple[str, bool]: pr_data = _r.json() labels = [label["name"] for label in pr_data["labels"]] if SKIP_NEWS_LABEL in labels: - description = f"'{SKIP_NEWS_LABEL}' label found" + status = (f"'{SKIP_NEWS_LABEL}' label found", True) else: if not in_next_dir: - description = f'No news entry in {NEWS_NEXT_DIR} or "{SKIP_NEWS_LABEL}" label found' + status = (f'No news entry in {NEWS_NEXT_DIR} or "{SKIP_NEWS_LABEL}" label found', False) elif not file_found: - description = "News entry not in an appropriate directory" + status = ("News entry not in an appropriate directory", False) else: - description = "News entry file name incorrectly formatted" - - status = (description, False) + status = ("News entry file name incorrectly formatted", False) return status diff --git a/scripts/news/utils.py b/scripts/news/utils.py index a2ff36dc..c49b8237 100644 --- a/scripts/news/utils.py +++ b/scripts/news/utils.py @@ -18,7 +18,7 @@ def nonceify(body: str) -> str: """ - Nonceify the changelog body! + Nonceify the news body! Generate hopefully-unique string of characters meant to prevent filename collisions. by computing the MD5 hash of the text, converting it to base64 (using the "urlsafe" alphabet), and taking the first @@ -55,6 +55,8 @@ def err(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: class NotRequiredIf(Option): + """Custom option class to make option mutually exclusive with another i.e. 'not_required_if'.""" + def __init__(self, *args, **kwargs): self.not_required_if = kwargs.pop("not_required_if") assert self.not_required_if, "'not_required_if' parameter required" # noqa: S101 @@ -64,7 +66,10 @@ def __init__(self, *args, **kwargs): ).strip() super(NotRequiredIf, self).__init__(*args, **kwargs) - def handle_parse_result(self, ctx: Context, opts: Mapping[str, Any], args: List[str]): + def handle_parse_result( + self, ctx: Context, opts: Mapping[str, Any], args: List[str] + ) -> Tuple[Any, List[str]]: + """Check if option is mutually exclusive with another, if yes print error and exist.""" we_are_present = self.name in opts other_present = self.not_required_if in opts @@ -82,12 +87,13 @@ def handle_parse_result(self, ctx: Context, opts: Mapping[str, Any], args: List[ return super(NotRequiredIf, self).handle_parse_result(ctx, opts, args) -def sanitize_section(section): +def sanitize_section(section: str) -> str: """Cleans up a section string, making it viable as a directory name.""" return section.replace("/", "-").lower() def glob_fragments(version: str, sections: List[str]) -> List[str]: + """Glob all news fragments present on the repo.""" filenames = [] base = os.path.join("news", version) @@ -108,7 +114,7 @@ def glob_fragments(version: str, sections: List[str]) -> List[str]: def get_metadata_from_file(path: Path) -> dict: - # path = Path(Path.cwd(), f"news/next/pr-{self.gh_pr}.{self.news_type}.{self.nonce}.md") + """Get metadata information from a news entry.""" new_fragment_file = path.stem date, gh_pr, news_type, nonce = new_fragment_file.split(".") @@ -126,6 +132,7 @@ def get_metadata_from_file(path: Path) -> dict: def get_project_meta() -> Tuple[str, str]: + """Get the project version and name from pyproject.toml file.""" with open("pyproject.toml", "rb") as pyproject: file_contents = tomli.load(pyproject) @@ -135,13 +142,14 @@ def get_project_meta() -> Tuple[str, str]: def load_toml_config() -> Dict[str, Any]: + """Load the news TOML configuration file and exit if found to be invalid.""" config_path = Path(Path.cwd(), "scripts/news/config.toml") try: with open(config_path, mode="r") as file: toml_dict = tomli.loads(file.read()) except tomli.TOMLDecodeError as e: - message = "Invalid changelog news configuration at {}\n{}".format( + message = "Invalid news configuration at {0}\n{1}".format( config_path, "".join(traceback.format_exception_only(type(e), e)), ) @@ -158,7 +166,7 @@ def render_fragments( wrap: bool, version_data: Tuple[str, str], date: Union[str, datetime.datetime], -): +) -> str: """Render the fragments into a news file.""" print(template) with open(template, mode="r") as template_file: diff --git a/tox.ini b/tox.ini index bc557a04..87115ef2 100644 --- a/tox.ini +++ b/tox.ini @@ -28,6 +28,7 @@ ignore= per-file-ignores= tests/*:,ANN,S101,F401 docs.py:B008 + scripts/news/*:S404,S603,S607 [isort] profile=black From bee0dab42b6cc2a7758ca1d60e2114f46cffa0b2 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Wed, 20 Oct 2021 05:43:08 +0530 Subject: [PATCH 14/27] Remove news type from path as it is already inside folder --- scripts/news/__main__.py | 2 +- scripts/news/check_news_workflow/__main__.py | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index 4c0e9971..5c7b4394 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -50,7 +50,7 @@ def save_news_fragment(ctx: click.Context, gh_pr: int, nonce: str, news_entry: s date = datetime.now(timezone.utc).strftime("%Y-%m-%d") path = Path( Path.cwd(), - f"news/next/{news_type}/{date}.pr-{str(gh_pr)}.{news_type}.{nonce}.md", + f"news/next/{news_type}/{date}.pr-{str(gh_pr)}.{nonce}.md", ) if not path.parents[1].exists(): err(NO_NEWS_PATH_ERROR, fg="blue") diff --git a/scripts/news/check_news_workflow/__main__.py b/scripts/news/check_news_workflow/__main__.py index 718344eb..7ff6be5c 100644 --- a/scripts/news/check_news_workflow/__main__.py +++ b/scripts/news/check_news_workflow/__main__.py @@ -32,13 +32,9 @@ def load_toml_config() -> dict: return toml_dict -CONFIG = load_toml_config() -SECTIONS = [_type for _type, _ in CONFIG.get("types").items()] - FILENAME_RE = re.compile( r"\d{4}-\d{2}-\d{2}(?:-\d{2}-\d{2}-\d{2})?\." # match `yyyy-mm-dd` or `yyyy-m-d` r"pr-\d+(?:,\d+)*\." # Issue number(s) - fr"({'|'.join(SECTIONS)})\." # Section type r"[A-Za-z0-9_=-]+\." # Nonce (URL-safe base64) r"md", # File extension re.VERBOSE, From 6555c4b8deb5c1ae17fc106761548d1502d413b4 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 21 Oct 2021 10:01:45 +0530 Subject: [PATCH 15/27] Remove binary pycache files --- scripts/news/__pycache__/__init__.cpython-39.pyc | Bin 237 -> 0 bytes scripts/news/__pycache__/__main__.cpython-39.pyc | Bin 7809 -> 0 bytes scripts/news/__pycache__/utils.cpython-39.pyc | Bin 6251 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 scripts/news/__pycache__/__init__.cpython-39.pyc delete mode 100644 scripts/news/__pycache__/__main__.cpython-39.pyc delete mode 100644 scripts/news/__pycache__/utils.cpython-39.pyc diff --git a/scripts/news/__pycache__/__init__.cpython-39.pyc b/scripts/news/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index c534847555f6b95d910612e7e2e32158799b0943..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 237 zcmYe~<>g`kg6$G{iB3TJF^Gc<7=auIATH(s5-AK(3@MCJj44dP44TZPqCg@4428UW zMTHOZCoKgLQ^2e$Rs%f)JwrcDrYHf|pdkOCc;8_6_<$f+H_wP$-0|^csYS(^`FZj2 zD;bKIfQEpHU*7r|`MIh3#Tl7piFw5t`T<4x=|zdTxtV$C`URCG89)sMMfq8&$tA`5 zx%nx%iJ3Y2#mPmP1wejYYI(7Ke0*kJW=VX!UP0w84jZ77(wtN~kOzuEmas4Z0I_mH AwEzGB diff --git a/scripts/news/__pycache__/__main__.cpython-39.pyc b/scripts/news/__pycache__/__main__.cpython-39.pyc deleted file mode 100644 index 301a9536dcb41f7d74a20188e74a932745d6eca4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7809 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 diff --git a/scripts/news/__pycache__/utils.cpython-39.pyc b/scripts/news/__pycache__/utils.cpython-39.pyc deleted file mode 100644 index af48b881982e3a25bd55a42294b54a74db65bf0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6251 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 From 6e21e339dadde47f1dad7a925577a441e3d7d754 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Mon, 25 Oct 2021 05:39:43 +0530 Subject: [PATCH 16/27] Add reqeusts to custom changelogger dev deps --- poetry.lock | 2 +- pyproject.toml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index b4eaa0ae..a23ff3eb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1207,7 +1207,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "e99a8dc25d0c2d30dd939af8e60aa92c8e4851614508fb46387d0663a0a8c261" +content-hash = "214de83a570696fe6e7292d65e9f766447546b3129df48f8f798c718ecef2184" [metadata.files] aiodns = [ diff --git a/pyproject.toml b/pyproject.toml index aa3423f0..16bc94f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,8 +56,10 @@ pytest-xdist = { version = "^2.3.0", extras = ["psutil"] } mkdocs = ">=1.1.2,<2.0.0" mkdocs-material = ">=7.1.9,<8.0.0" mkdocs-markdownextradata-plugin = ">=0.1.7,<0.2.0" +# Custom changelogger click = "^8.0.3" Jinja2 = "^3.0.2" +requests = "^2.26.0" [build-system] requires = ["poetry-core>=1.0.0"] From 30a5155cecc666be081c6ec3d5911cd08ab06da7 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Mon, 25 Oct 2021 05:40:36 +0530 Subject: [PATCH 17/27] Correct default template file extension --- scripts/news/__main__.py | 2 +- .../{default_template.rst.jinja => default_template.md.jinja} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename scripts/news/{default_template.rst.jinja => default_template.md.jinja} (100%) diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index 5c7b4394..eecfc509 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -201,7 +201,7 @@ def cli_build_news(ctx: click.Context) -> None: template = CONFIG["core"].get("template") if not template: - template = Path(Path.cwd(), "scripts/news/default_template.rst.jinja") + template = Path(Path.cwd(), "scripts/news/default_template.md.jinja") else: template = Path(Path.cwd(), f"scripts/news/{template}") diff --git a/scripts/news/default_template.rst.jinja b/scripts/news/default_template.md.jinja similarity index 100% rename from scripts/news/default_template.rst.jinja rename to scripts/news/default_template.md.jinja From 6459a7a009d8803d025a69062ac6f24de84f09d0 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 4 Nov 2021 02:51:07 +0530 Subject: [PATCH 18/27] Add documentationfor news management tool --- pyproject.toml | 1 + scripts/news/README.md | 25 +++++++++++++++++++ scripts/news/__main__.py | 2 +- scripts/news/config.toml | 3 --- ...lt_template.md.jinja => template.md.jinja} | 0 5 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 scripts/news/README.md rename scripts/news/{default_template.md.jinja => template.md.jinja} (100%) diff --git a/pyproject.toml b/pyproject.toml index 16bc94f5..ebc2f6b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -93,3 +93,4 @@ lint = { cmd = "pre-commit run --all-files", help = "Checks all files for CI err precommit = { cmd = "pre-commit install --install-hooks", help = "Installs the precommit hook" } report = { cmd = "coverage report", help = "Show coverage report from previously run tests." } test = { cmd = "pytest -n auto --dist loadfile", help = "Runs tests and save results to a coverage report" } +news-create = { cmd = "python -m scripts.news add", help = "Create a new news fragment for your PR to discord-modmail!"} diff --git a/scripts/news/README.md b/scripts/news/README.md new file mode 100644 index 00000000..24232e9f --- /dev/null +++ b/scripts/news/README.md @@ -0,0 +1,25 @@ +# News Management Tool + +This writes a news fragment into a directory called `news/next`, here `next` corresponds to the next version +of `discord-modmail`. If the directory doesn't exist, it means you are in the wrong CWD (current working directory) +or you are running the command for the first time, in which case you need to manually create the folder. + +To make a new news fragment, you can run `python -m scripts.news add` or use the poetry task `news-create`. It +will make a new file with a filename using the current date and time, GitHub pull request number and a *hopefully +unique* 6-letter word generated by encoding the news entry text. + +When it is time to release `discord-modmail` 🎉, the `build` command would aggregate all the fragments present +in `news/next` directory into a single news file for that version. + + +## Concepts + +### Fragments + +Fragments are files describing your latest work, created by the `python -m scripts.news add` command. The files +are created in the `news/next` directory. + +### Categories / Types +News entries can be categorized, for example as additions, fixes, removals, and breaking changes. The list +of categories is settable with the types setting in [config.toml](./config.toml). While making a news fragment +entry using the `add` command, you would be asked to specify the `type`. diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index eecfc509..dcd14965 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -201,7 +201,7 @@ def cli_build_news(ctx: click.Context) -> None: template = CONFIG["core"].get("template") if not template: - template = Path(Path.cwd(), "scripts/news/default_template.md.jinja") + template = Path(Path.cwd(), "scripts/news/template.md.jinja") else: template = Path(Path.cwd(), f"scripts/news/{template}") diff --git a/scripts/news/config.toml b/scripts/news/config.toml index a77b70de..ebb543ec 100644 --- a/scripts/news/config.toml +++ b/scripts/news/config.toml @@ -1,6 +1,3 @@ -[core] -template = "default_template.rst" - [types] feature = { name = "Features" } trivial = { name = "Trivial/Internal Changes" } diff --git a/scripts/news/default_template.md.jinja b/scripts/news/template.md.jinja similarity index 100% rename from scripts/news/default_template.md.jinja rename to scripts/news/template.md.jinja From 384ea8948fcadc522387ad4bd52e9919f4495677 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 4 Nov 2021 03:07:04 +0530 Subject: [PATCH 19/27] Add option to edit/keep fragments while building --- scripts/news/__main__.py | 33 +++++++++++++++++++-------------- scripts/news/utils.py | 5 ++--- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index dcd14965..4d86ab85 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -40,7 +40,6 @@ "doesn't exist please create it and run this command again :) Happy change-logging!" ) - CONFIG = load_toml_config() SECTIONS = [_type for _type, _ in CONFIG.get("types").items()] @@ -48,10 +47,7 @@ def save_news_fragment(ctx: click.Context, gh_pr: int, nonce: str, news_entry: str, news_type: str) -> None: """Save received changelog data to a news file.""" date = datetime.now(timezone.utc).strftime("%Y-%m-%d") - path = Path( - Path.cwd(), - f"news/next/{news_type}/{date}.pr-{str(gh_pr)}.{nonce}.md", - ) + path = Path(Path.cwd(), f"news/next/{news_type}/{date}.pr-{gh_pr}.{nonce}.md") if not path.parents[1].exists(): err(NO_NEWS_PATH_ERROR, fg="blue") ctx.exit(1) @@ -158,11 +154,11 @@ def cli_add_news(ctx: click.Context, message: str, editor: str, type: str, pr_nu ) if not content: - message_notes = "# ERROR: No content found previously" + message_notes = ["# ERROR: No content found previously"] continue message = "\n".join( - [line.rstrip() for line in content.split("\n") if not line.lstrip().startswith("#")] + line.rstrip() for line in content.split("\n") if not line.lstrip().startswith("#") ) if message is None: @@ -175,8 +171,14 @@ def cli_add_news(ctx: click.Context, message: str, editor: str, type: str, pr_nu @cli_main.command("build") +@click.option( + "--edit/--no-edit", + default=None, + help="Open the changelog file in your text editor.", +) +@click.option("--keep", is_flag=True, help="Keep the fragment files that are collected.") @click.pass_context -def cli_build_news(ctx: click.Context) -> None: +def cli_build_news(ctx: click.Context, edit: Optional[bool], keep: bool) -> None: """Build a combined news file 📜 from news fragments.""" filenames = glob_fragments("next", SECTIONS) _file_metadata = {} @@ -222,16 +224,19 @@ def cli_build_news(ctx: click.Context) -> None: ) news_path = Path(Path.cwd(), f"news/{version}.md") - with open(news_path, mode="w") as filename: - filename.write(version_news) + with open(news_path, mode="w") as file: + file.write(version_news) out(f"All done! ✨ 🍰 ✨ Created {name}-v{version} news at {news_path}") - files = Path(Path.cwd(), "scripts/news/next") - for news_fragment in files.glob("*.md"): - os.remove(news_fragment) + if edit: + click.edit(filename=str(news_path)) - out("🍰 Cleared existing `scripts/news/next` news fragments!") + if not keep: + files = Path(Path.cwd(), "scripts/news/next") + for news_fragment in files.glob("*.md"): + os.remove(news_fragment) + out("🍰 Cleared existing `scripts/news/next` news fragments!") if __name__ == "__main__": diff --git a/scripts/news/utils.py b/scripts/news/utils.py index c49b8237..d4849d43 100644 --- a/scripts/news/utils.py +++ b/scripts/news/utils.py @@ -70,10 +70,10 @@ def handle_parse_result( self, ctx: Context, opts: Mapping[str, Any], args: List[str] ) -> Tuple[Any, List[str]]: """Check if option is mutually exclusive with another, if yes print error and exist.""" - we_are_present = self.name in opts other_present = self.not_required_if in opts if other_present: + we_are_present = self.name in opts if we_are_present: err( f"{ERROR_MSG_PREFIX} Illegal usage. `%s` is mutually exclusive with `%s`" @@ -121,14 +121,13 @@ def get_metadata_from_file(path: Path) -> dict: with open(path, "r", encoding="utf-8") as file: news_entry = file.read() - metadata = { + return { "date": date, "gh_pr": gh_pr, "news_type": news_type, "nonce": nonce, "news_entry": news_entry, } - return metadata def get_project_meta() -> Tuple[str, str]: From 1b2e521052d5923fc264d39637bca7e54a261893 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 23 Nov 2021 17:49:42 +0530 Subject: [PATCH 20/27] Address reviews --- news/next/.gitkeep | 0 news/next/README.md | 1 + scripts/news/__init__.py | 2 - scripts/news/__main__.py | 138 +++---------------- scripts/news/check_news_workflow/__main__.py | 25 +--- scripts/news/config.toml | 9 -- scripts/news/constants.py | 38 +++++ scripts/news/template.md.jinja | 2 +- scripts/news/utils.py | 110 ++++++++++----- tox.ini | 2 +- 10 files changed, 138 insertions(+), 189 deletions(-) create mode 100644 news/next/.gitkeep create mode 100644 news/next/README.md delete mode 100644 scripts/news/config.toml create mode 100644 scripts/news/constants.py diff --git a/news/next/.gitkeep b/news/next/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/news/next/README.md b/news/next/README.md new file mode 100644 index 00000000..cc7c1ca7 --- /dev/null +++ b/news/next/README.md @@ -0,0 +1 @@ +All your changelogs for upcoming release! diff --git a/scripts/news/__init__.py b/scripts/news/__init__.py index 69a40730..f102a9ca 100644 --- a/scripts/news/__init__.py +++ b/scripts/news/__init__.py @@ -1,3 +1 @@ -ERROR_MSG_PREFIX = "Oh no! 💥 💔 💥" - __version__ = "0.0.1" diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index 4d86ab85..c0693971 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -1,104 +1,18 @@ -import os -import subprocess from collections import defaultdict from datetime import datetime, timezone -from pathlib import Path from typing import Optional import click -import requests - -from . import ERROR_MSG_PREFIX, __version__ -from .utils import ( - NotRequiredIf, - err, - get_metadata_from_file, - get_project_meta, - glob_fragments, - load_toml_config, - nonceify, - out, - render_fragments, -) - - -PR_ENDPOINT = "https://api.github.com/repos/discord-modmail/modmail/pulls/{number}" -BAD_RESPONSE = { - 404: "Pull request not located! Please enter a valid number!", - 403: "Rate limit has been hit! Please try again later!", -} - -TEMPLATE = """ -# Please write your news content. When finished, save the file. -# In order to abort, exit without saving. -# Lines starting with \"#\" are ignored. - -""".lstrip() -NO_NEWS_PATH_ERROR = ( - f"{ERROR_MSG_PREFIX} `news/next/` doesn't exist.\nYou are either in the wrong directory while" - " running this command (should be in the project root) or the path doesn't exist, if it " - "doesn't exist please create it and run this command again :) Happy change-logging!" -) -CONFIG = load_toml_config() -SECTIONS = [_type for _type, _ in CONFIG.get("types").items()] - - -def save_news_fragment(ctx: click.Context, gh_pr: int, nonce: str, news_entry: str, news_type: str) -> None: - """Save received changelog data to a news file.""" - date = datetime.now(timezone.utc).strftime("%Y-%m-%d") - path = Path(Path.cwd(), f"news/next/{news_type}/{date}.pr-{gh_pr}.{nonce}.md") - if not path.parents[1].exists(): - err(NO_NEWS_PATH_ERROR, fg="blue") - ctx.exit(1) - elif not path.parents[0].exists(): - make_news_type_dir = click.confirm( - f"Should I make the new type DIR for the news type at {path.parents[0]}" - ) - if make_news_type_dir: - path.parents[0].mkdir(exist_ok=True) - elif path.exists(): - # The file exists - err(f"{ERROR_MSG_PREFIX} {Path(os.path.relpath(path, start=Path.cwd()))} already exists") - ctx.exit(1) - - text = str(news_entry) - with open(path, "wt", encoding="utf-8") as file: - file.write(text) - - # Add news fragment to git stage - subprocess.run(["git", "add", "--force", path]).check_returncode() - - out( - f"All done! ✨ 🍰 ✨ Created news fragment at {Path(os.path.relpath(path, start=Path.cwd()))}" - "\nYou are now ready for commit!" - ) - - -def validate_pull_request_number( - ctx: click.Context, _param: click.Parameter, value: Optional[int] -) -> Optional[int]: - """Check if the given pull request number exists on the github repository.""" - r = requests.get(PR_ENDPOINT.format(number=value)) - if r.status_code == 403: - if r.headers.get("X-RateLimit-Remaining") == "0": - err(f"{ERROR_MSG_PREFIX} Ratelimit reached, please retry in a few minutes.") - ctx.exit() - err(f"{ERROR_MSG_PREFIX} Cannot access pull request.") - ctx.exit() - elif r.status_code in (404, 410): - err(f"{ERROR_MSG_PREFIX} PR not found.") - ctx.exit() - elif r.status_code != 200: - err(f"{ERROR_MSG_PREFIX} Error while fetching issue, retry again after sometime.") - ctx.exit() - - return value +from . import __version__ +from .constants import * +from .utils import * @click.group(context_settings=dict(help_option_names=["-h", "--help"]), invoke_without_command=True) @click.version_option(version=__version__) -def cli_main() -> None: +@click.pass_context +def cli_main(ctx: click.Context) -> None: """ Modmail News 📜🤖. @@ -107,7 +21,9 @@ def cli_main() -> None: contributors and maintainers to work with news files (changelogs) by automating the process of generating, compiling and validating them! """ - ... + if not ctx.args and not ctx.resilient_parsing and not ctx.command: + click.echo(ctx.get_help()) + ctx.exit() @cli_main.command("add") @@ -131,24 +47,19 @@ def cli_main() -> None: prompt=True, ) @click.option( - "--pr-number", + "--pr", type=int, prompt=True, callback=validate_pull_request_number, ) @click.pass_context -def cli_add_news(ctx: click.Context, message: str, editor: str, type: str, pr_number: int) -> None: +def cli_add_news(ctx: click.Context, message: str, editor: str, type: str, pr: int) -> None: """Add a news entry 📜 to the current discord-modmail repo for your awesome change!""" if not message: message_notes = [] while True: content = click.edit( - ( - "# Please write your news content. When finished, save the file.\n" - "# In order to abort, exit without saving.\n" - "# Lines starting with '#' are ignored.\n" - "\n".join(message_notes) - ), + "\n".join((TEMPLATE, *message_notes)), editor=editor, extension="md", ) @@ -167,7 +78,7 @@ def cli_add_news(ctx: click.Context, message: str, editor: str, type: str, pr_nu break - save_news_fragment(ctx, pr_number, nonceify(message), message, type) + save_news_fragment(ctx, pr, nonceify(message), message, type) @cli_main.command("build") @@ -192,7 +103,7 @@ def cli_build_news(ctx: click.Context, edit: Optional[bool], keep: bool) -> None for filename in filenames: if not filename.endswith(".md"): continue - _file_metadata[filename] = get_metadata_from_file(Path(filename)) + _file_metadata[filename] = get_metadata_from_news(Path(filename)) # Group metadata according to news_type for path, fragment in _file_metadata.items(): @@ -201,28 +112,16 @@ def cli_build_news(ctx: click.Context, edit: Optional[bool], keep: bool) -> None fragment["path"] = path file_metadata[news_type].append(fragment) - template = CONFIG["core"].get("template") - if not template: - template = Path(Path.cwd(), "scripts/news/template.md.jinja") - else: - template = Path(Path.cwd(), f"scripts/news/{template}") - - if not template.exists(): - err( - f"{ERROR_MSG_PREFIX} Template at {template.relative_to(Path.cwd())} not found :(. Make sure " - f"your path is relative to `scripts/news`!" - ) - name, version = get_project_meta() version_news = render_fragments( - section_names=CONFIG["types"], - template=template, + sections=SECTIONS, + template=TEMPLATE_FILE_PATH, metadata=file_metadata, wrap=True, version_data=(name, version), date=date, ) - news_path = Path(Path.cwd(), f"news/{version}.md") + news_path = Path(REPO_ROOT, f"news/{version}.md") with open(news_path, mode="w") as file: file.write(version_news) @@ -233,10 +132,9 @@ def cli_build_news(ctx: click.Context, edit: Optional[bool], keep: bool) -> None click.edit(filename=str(news_path)) if not keep: - files = Path(Path.cwd(), "scripts/news/next") - for news_fragment in files.glob("*.md"): + for news_fragment in NEWS_NEXT.glob("*/*.md"): os.remove(news_fragment) - out("🍰 Cleared existing `scripts/news/next` news fragments!") + out("🍰 Cleared existing `news/next` news fragments!") if __name__ == "__main__": diff --git a/scripts/news/check_news_workflow/__main__.py b/scripts/news/check_news_workflow/__main__.py index 7ff6be5c..0d56934a 100644 --- a/scripts/news/check_news_workflow/__main__.py +++ b/scripts/news/check_news_workflow/__main__.py @@ -1,35 +1,16 @@ import pathlib import re import sys -import traceback from typing import Tuple import requests -import tomli NEWS_NEXT_DIR = "news/next/" SKIP_NEWS_LABEL = "skip changelog" GH_API_URL = "https://api.github.com/" HEADERS = {"accept": "application/vnd.github.v3+json"} - - -def load_toml_config() -> dict: - """Load the news TOML configuration file and exit if found to be invalid.""" - config_path = pathlib.Path(pathlib.Path.cwd(), "scripts/news/config.toml") - - try: - with open(config_path, mode="r") as file: - toml_dict = tomli.loads(file.read()) - except tomli.TOMLDecodeError as e: - message = "Invalid news configuration at {0}\n{1}".format( - config_path, - "".join(traceback.format_exception_only(type(e), e)), - ) - print(message) - sys.exit(1) - else: - return toml_dict +REPO_ORG_NAME = "discord-modmail/modmail" FILENAME_RE = re.compile( @@ -48,7 +29,7 @@ def is_news_dir(filename: str) -> bool: def main(pr: int) -> Tuple[str, bool]: """Main function to check for a changelog entry.""" - r = requests.get(f"{GH_API_URL}repos/discord-modmail/modmail/pulls/{pr}/files", headers=HEADERS) + r = requests.get(f"{GH_API_URL}repos/{REPO_ORG_NAME}/pulls/{pr}/files", headers=HEADERS) files_changed = r.json() in_next_dir = file_found = False @@ -64,7 +45,7 @@ def main(pr: int) -> Tuple[str, bool]: status = (f"News entry found in {NEWS_NEXT_DIR}", True) break else: - _r = requests.get(f"{GH_API_URL}repos/discord-modmail/modmail/pulls/{pr}", headers=HEADERS) + _r = requests.get(f"{GH_API_URL}repos/{REPO_ORG_NAME}/pulls/{pr}", headers=HEADERS) pr_data = _r.json() labels = [label["name"] for label in pr_data["labels"]] if SKIP_NEWS_LABEL in labels: diff --git a/scripts/news/config.toml b/scripts/news/config.toml deleted file mode 100644 index ebb543ec..00000000 --- a/scripts/news/config.toml +++ /dev/null @@ -1,9 +0,0 @@ -[types] -feature = { name = "Features" } -trivial = { name = "Trivial/Internal Changes" } -improvement = { name = "Improvements" } -bugfix = { name = "Bug Fixes" } -doc = { name = "Improved Documentation" } -deprecation = { name = "Deprecations" } -breaking = { name = "Breaking Changes" } -internal = { name = "Internal" } diff --git a/scripts/news/constants.py b/scripts/news/constants.py new file mode 100644 index 00000000..e8342cc2 --- /dev/null +++ b/scripts/news/constants.py @@ -0,0 +1,38 @@ +import os +from pathlib import Path + +import modmail + + +PR_ENDPOINT = "https://api.github.com/repos/discord-modmail/modmail/pulls/{number}" +BAD_RESPONSE = { + 404: "Pull request not located! Please enter a valid number!", + 403: "Rate limit has been hit! Please try again later!", +} + +TEMPLATE_FILE_PATH = Path(Path(__file__).parent, "template.md.jinja") +REPO_ROOT = Path(modmail.__file__).parent.parent +NEWS_NEXT = Path(REPO_ROOT, "news/next") + +ERROR_MSG_PREFIX = "Oh no! 💥 💔 💥" +TEMPLATE = """ +# Please write your news content. When finished, save the file. +# In order to abort, exit without saving. +# Lines starting with "#" are ignored. + +""".lstrip() +NO_NEWS_PATH_ERROR = ( + f"{ERROR_MSG_PREFIX} {Path(os.path.relpath(NEWS_NEXT, start=Path.cwd()))} doesn't exist, please create it" + f" and run this command again :) Happy change-logging!" +) + +SECTIONS = { + "feature": "Features", + "trivial": "Trivial/Internal Changes", + "improvement": "Improvements", + "bugfix": "Bug Fixes", + "doc": "Improved Documentation", + "deprecation": "Deprecations", + "breaking": "Breaking Changes", + "internal": "Internal", +} diff --git a/scripts/news/template.md.jinja b/scripts/news/template.md.jinja index c5be05d2..feab6991 100644 --- a/scripts/news/template.md.jinja +++ b/scripts/news/template.md.jinja @@ -9,7 +9,7 @@ --- {% for section, fragments in metadata.items() %} -### {{ section_names[section] }} +### {{ sections[section] }} {% for fragment in fragments %} - {{ fragment["news_entry"].strip("\n") }} (#{{ fragment["gh_pr"].split("-")[1] }}) {% endfor %} diff --git a/scripts/news/utils.py b/scripts/news/utils.py index d4849d43..4508f0da 100644 --- a/scripts/news/utils.py +++ b/scripts/news/utils.py @@ -2,18 +2,30 @@ import datetime import glob import hashlib -import os -import sys import textwrap -import traceback -from pathlib import Path from typing import Any, Dict, List, Mapping, Optional, Tuple, Union +import click +import requests import tomli from click import Context, Option, echo, style from jinja2 import Template -from . import ERROR_MSG_PREFIX +from .constants import * + + +__all__ = ( + "NotRequiredIf", + "get_metadata_from_news", + "get_project_meta", + "glob_fragments", + "err", + "nonceify", + "out", + "render_fragments", + "save_news_fragment", + "validate_pull_request_number", +) def nonceify(body: str) -> str: @@ -64,7 +76,7 @@ def __init__(self, *args, **kwargs): kwargs.get("help", "") + " NOTE: This argument is mutually exclusive with %s" % self.not_required_if ).strip() - super(NotRequiredIf, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def handle_parse_result( self, ctx: Context, opts: Mapping[str, Any], args: List[str] @@ -84,7 +96,7 @@ def handle_parse_result( else: self.prompt = None - return super(NotRequiredIf, self).handle_parse_result(ctx, opts, args) + return super().handle_parse_result(ctx, opts, args) def sanitize_section(section: str) -> str: @@ -92,12 +104,12 @@ def sanitize_section(section: str) -> str: return section.replace("/", "-").lower() -def glob_fragments(version: str, sections: List[str]) -> List[str]: +def glob_fragments(version: str, sections: Dict[str, str]) -> List[str]: """Glob all news fragments present on the repo.""" filenames = [] base = os.path.join("news", version) - if version != "next": + if version.lower() != "next": wildcard = base + ".md" filenames.extend(glob.glob(wildcard)) else: @@ -105,18 +117,17 @@ def glob_fragments(version: str, sections: List[str]) -> List[str]: wildcard = os.path.join(base, sanitize_section(section), "*.md") entries = glob.glob(wildcard) entries.sort(reverse=True) - deletables = [x for x in entries if x.endswith("/README.md")] - for filename in deletables: - entries.remove(filename) + entries = [x for x in entries if not x.endswith("/README.md")] filenames.extend(entries) return filenames -def get_metadata_from_file(path: Path) -> dict: +def get_metadata_from_news(path: Path) -> dict: """Get metadata information from a news entry.""" new_fragment_file = path.stem - date, gh_pr, news_type, nonce = new_fragment_file.split(".") + date, gh_pr, nonce = new_fragment_file.split(".") + news_type = path.parent.name with open(path, "r", encoding="utf-8") as file: news_entry = file.read() @@ -140,26 +151,8 @@ def get_project_meta() -> Tuple[str, str]: return name, version -def load_toml_config() -> Dict[str, Any]: - """Load the news TOML configuration file and exit if found to be invalid.""" - config_path = Path(Path.cwd(), "scripts/news/config.toml") - - try: - with open(config_path, mode="r") as file: - toml_dict = tomli.loads(file.read()) - except tomli.TOMLDecodeError as e: - message = "Invalid news configuration at {0}\n{1}".format( - config_path, - "".join(traceback.format_exception_only(type(e), e)), - ) - err(message) - sys.exit(1) - else: - return toml_dict - - def render_fragments( - section_names: Dict[str, dict], + sections: Dict[str, str], template: Path, metadata: Dict[str, list], wrap: bool, @@ -173,7 +166,7 @@ def render_fragments( version_data = {"name": version_data[0], "version": version_data[1], "date": date} res = jinja_template.render( - section_names={_type: config["name"] for _type, config in section_names.items()}, + sections=sections.copy(), version_data=version_data, metadata=metadata, ) @@ -194,3 +187,52 @@ def render_fragments( done.append(line) return "\n".join(done).rstrip() + "\n" + + +def save_news_fragment(ctx: click.Context, gh_pr: int, nonce: str, news_entry: str, news_type: str) -> None: + """Save received changelog data to a news file.""" + date = datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d") + path = Path(REPO_ROOT, f"news/next/{news_type}/{date}.pr-{gh_pr}.{nonce}.md") + if not path.parents[1].exists(): + err(NO_NEWS_PATH_ERROR, fg="blue") + ctx.exit(1) + elif not path.parents[0].exists(): + make_news_type_dir = click.confirm( + f"Should I make the new type DIR for the news type at {path.parents[0]}" + ) + if make_news_type_dir: + path.parents[0].mkdir(exist_ok=True) + elif path.exists(): + # The file exists + err(f"{ERROR_MSG_PREFIX} {Path(os.path.relpath(path, start=Path.cwd()))} already exists") + ctx.exit(1) + + text = str(news_entry) + with open(path, "wt", encoding="utf-8") as file: + file.write(text) + + out( + f"All done! ✨ 🍰 ✨ Created news fragment at {Path(os.path.relpath(path, start=Path.cwd()))}" + "\nYou are now ready for commit the changelog!" + ) + + +def validate_pull_request_number( + ctx: click.Context, _param: click.Parameter, value: Optional[int] +) -> Optional[int]: + """Check if the given pull request number exists on the github repository.""" + r = requests.get(PR_ENDPOINT.format(number=value)) + if r.status_code == 403: + if r.headers.get("X-RateLimit-Remaining") == "0": + err(f"{ERROR_MSG_PREFIX} Ratelimit reached, please retry in a few minutes.") + ctx.exit() + err(f"{ERROR_MSG_PREFIX} Cannot access pull request.") + ctx.exit() + elif r.status_code in (404, 410): + err(f"{ERROR_MSG_PREFIX} PR not found.") + ctx.exit() + elif r.status_code != 200: + err(f"{ERROR_MSG_PREFIX} Error while fetching issue, retry again after sometime.") + ctx.exit() + + return value diff --git a/tox.ini b/tox.ini index 87115ef2..70c6a70b 100644 --- a/tox.ini +++ b/tox.ini @@ -28,7 +28,7 @@ ignore= per-file-ignores= tests/*:,ANN,S101,F401 docs.py:B008 - scripts/news/*:S404,S603,S607 + scripts/news/*:F405,F403 [isort] profile=black From 58643f92e2334e58761afeebc2d87ba7f768fc19 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Wed, 24 Nov 2021 05:09:22 +0530 Subject: [PATCH 21/27] Use incrementing rather than nonceify --- scripts/news/__main__.py | 2 +- scripts/news/utils.py | 66 ++++++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index c0693971..01e9002c 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -78,7 +78,7 @@ def cli_add_news(ctx: click.Context, message: str, editor: str, type: str, pr: i break - save_news_fragment(ctx, pr, nonceify(message), message, type) + save_news_fragment(ctx, pr, message, type) @cli_main.command("build") diff --git a/scripts/news/utils.py b/scripts/news/utils.py index 4508f0da..a836882e 100644 --- a/scripts/news/utils.py +++ b/scripts/news/utils.py @@ -1,9 +1,9 @@ -import base64 -import datetime import glob -import hashlib +import itertools +import tempfile import textwrap -from typing import Any, Dict, List, Mapping, Optional, Tuple, Union +from datetime import datetime, timezone +from typing import Any, Dict, Generator, List, Mapping, Optional, Tuple, Union import click import requests @@ -20,7 +20,6 @@ "get_project_meta", "glob_fragments", "err", - "nonceify", "out", "render_fragments", "save_news_fragment", @@ -28,18 +27,6 @@ ) -def nonceify(body: str) -> str: - """ - Nonceify the news body! - - Generate hopefully-unique string of characters meant to prevent filename collisions. by computing the - MD5 hash of the text, converting it to base64 (using the "urlsafe" alphabet), and taking the first - 6 characters of that. - """ - digest = hashlib.md5(body.encode("utf-8")).digest() # noqa: S303 - return base64.urlsafe_b64encode(digest)[0:6].decode("ascii") - - def _out(message: Optional[str] = None, nl: bool = True, **styles: Any) -> None: if message is not None: if "bold" not in styles: @@ -126,7 +113,7 @@ def glob_fragments(version: str, sections: Dict[str, str]) -> List[str]: def get_metadata_from_news(path: Path) -> dict: """Get metadata information from a news entry.""" new_fragment_file = path.stem - date, gh_pr, nonce = new_fragment_file.split(".") + date, gh_pr, increment = new_fragment_file.partition(".") news_type = path.parent.name with open(path, "r", encoding="utf-8") as file: @@ -136,7 +123,7 @@ def get_metadata_from_news(path: Path) -> dict: "date": date, "gh_pr": gh_pr, "news_type": news_type, - "nonce": nonce, + "increment": increment, "news_entry": news_entry, } @@ -157,10 +144,9 @@ def render_fragments( metadata: Dict[str, list], wrap: bool, version_data: Tuple[str, str], - date: Union[str, datetime.datetime], + date: Union[str, datetime], ) -> str: """Render the fragments into a news file.""" - print(template) with open(template, mode="r") as template_file: jinja_template = Template(template_file.read(), trim_blocks=True) @@ -189,10 +175,29 @@ def render_fragments( return "\n".join(done).rstrip() + "\n" -def save_news_fragment(ctx: click.Context, gh_pr: int, nonce: str, news_entry: str, news_type: str) -> None: +def _uniquify(path: Path, sep: str = ".") -> Path: + """Expands name portion of filename with numeric '.(x)' suffix to return a unique path.""" + + def name_sequence() -> Generator[str, Any, None]: + count = itertools.count(start=1) + yield "" + while True: + yield "{s}{n:d}".format(s=sep, n=next(count)) + + orig = tempfile._name_sequence + with tempfile._once_lock: + tempfile._name_sequence = name_sequence() + dirname, filename, ext = path.parent, path.stem, path.suffix + fd, filename = tempfile.mkstemp(dir=dirname, prefix=filename, suffix=ext) + tempfile._name_sequence = orig + + return Path(filename) + + +def save_news_fragment(ctx: click.Context, gh_pr: int, news_entry: str, news_type: str) -> None: """Save received changelog data to a news file.""" - date = datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d") - path = Path(REPO_ROOT, f"news/next/{news_type}/{date}.pr-{gh_pr}.{nonce}.md") + date = datetime.now(timezone.utc).strftime("%Y-%m-%d") + path = Path(REPO_ROOT, f"news/next/{news_type}/{date}.pr-{gh_pr}.md") if not path.parents[1].exists(): err(NO_NEWS_PATH_ERROR, fg="blue") ctx.exit(1) @@ -203,9 +208,16 @@ def save_news_fragment(ctx: click.Context, gh_pr: int, nonce: str, news_entry: s if make_news_type_dir: path.parents[0].mkdir(exist_ok=True) elif path.exists(): - # The file exists - err(f"{ERROR_MSG_PREFIX} {Path(os.path.relpath(path, start=Path.cwd()))} already exists") - ctx.exit(1) + # a changelog for that PR already + one_more = click.prompt( + f"A changelog for this PR already exists " + f"at {Path(os.path.relpath(path, start=Path.cwd()))}, " + f"do you want to make another one?" + ) + if one_more: + path = _uniquify(path) + else: + ctx.exit(1) text = str(news_entry) with open(path, "wt", encoding="utf-8") as file: From aa11a32b9108fb38fbc07b81b38cbae610fbad7c Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Sat, 27 Nov 2021 01:49:46 -0500 Subject: [PATCH 22/27] deps: run poetry lock --no-update Signed-off-by: onerandomusername --- docs/.requirements.txt | 2 +- poetry.lock | 166 +++++++++++++---------------------------- 2 files changed, 51 insertions(+), 117 deletions(-) diff --git a/docs/.requirements.txt b/docs/.requirements.txt index 6cbc9e06..67122afd 100644 --- a/docs/.requirements.txt +++ b/docs/.requirements.txt @@ -3,5 +3,5 @@ # we have to declare our dependencies here mkdocs==1.2.3 -mkdocs-material==7.2.6 +mkdocs-material==7.3.6 mkdocs-markdownextradata-plugin==0.1.9 diff --git a/poetry.lock b/poetry.lock index 600109fe..b43e2fac 100644 --- a/poetry.lock +++ b/poetry.lock @@ -222,7 +222,7 @@ cron = ["capturer (>=2.4)"] [[package]] name = "coverage" -version = "6.1.1" +version = "6.2" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -269,6 +269,7 @@ voice = ["PyNaCl (>=1.3.0,<1.5)"] [package.source] type = "url" url = "https://github.com/Rapptz/discord.py/archive/master.zip" + [[package]] name = "distlib" version = "0.3.3" @@ -433,29 +434,6 @@ python-dateutil = ">=2.8.1" [package.extras] dev = ["twine", "markdown", "flake8", "wheel"] -[[package]] -name = "gitdb" -version = "4.0.9" -description = "Git Object Database" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.24" -description = "GitPython is a python library used to interact with Git repositories" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -gitdb = ">=4.0.1,<5" -typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""} - [[package]] name = "humanfriendly" version = "10.0" @@ -1037,14 +1015,6 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -[[package]] -name = "smmap" -version = "5.0.0" -description = "A pure Python implementation of a sliding window memory map manager" -category = "dev" -optional = false -python-versions = ">=3.6" - [[package]] name = "snowballstemmer" version = "2.1.0" @@ -1053,17 +1023,6 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "stevedore" -version = "3.5.0" -description = "Manage dynamic plugins for Python applications" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pbr = ">=2.0.0,<2.1.0 || >2.1.0" - [[package]] name = "taskipy" version = "1.9.0" @@ -1193,7 +1152,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "46c461ed3cbad313d4a2e501669bcc3718b82751d4f605367ac72944206a8009" +content-hash = "302945ab04d1b60ec2814b8a3a5d0492a63aac2ee61b160bded2277af00c7c04" [metadata.files] aiodns = [ @@ -1285,14 +1244,6 @@ brotlipy = [ {file = "brotlipy-0.7.0-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:79aaf217072840f3e9a3b641cccc51f7fc23037496bd71e26211856b93f4b4cb"}, {file = "brotlipy-0.7.0-cp34-cp34m-win32.whl", hash = "sha256:a07647886e24e2fb2d68ca8bf3ada398eb56fd8eac46c733d4d95c64d17f743b"}, {file = "brotlipy-0.7.0-cp34-cp34m-win_amd64.whl", hash = "sha256:c6cc0036b1304dd0073eec416cb2f6b9e37ac8296afd9e481cac3b1f07f9db25"}, - {file = "brotlipy-0.7.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:382971a641125323e90486244d6266ffb0e1f4dd920fbdcf508d2a19acc7c3b3"}, - {file = "brotlipy-0.7.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:82f61506d001e626ec3a1ac8a69df11eb3555a4878599befcb672c8178befac8"}, - {file = "brotlipy-0.7.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:7ff18e42f51ebc9d9d77a0db33f99ad95f01dd431e4491f0eca519b90e9415a9"}, - {file = "brotlipy-0.7.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:8ef230ca9e168ce2b7dc173a48a0cc3d78bcdf0bd0ea7743472a317041a4768e"}, - {file = "brotlipy-0.7.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:b7cf5bb69e767a59acc3da0d199d4b5d0c9fed7bef3ffa3efa80c6f39095686b"}, - {file = "brotlipy-0.7.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:e5c549ae5928dda952463196180445c24d6fad2d73cb13bd118293aced31b771"}, - {file = "brotlipy-0.7.0-cp35-abi3-win32.whl", hash = "sha256:79ab3bca8dd12c17e092273484f2ac48b906de2b4828dcdf6a7d520f99646ab3"}, - {file = "brotlipy-0.7.0-cp35-abi3-win_amd64.whl", hash = "sha256:ac1d66c9774ee62e762750e399a0c95e93b180e96179b645f28b162b55ae8adc"}, {file = "brotlipy-0.7.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:07194f4768eb62a4f4ea76b6d0df6ade185e24ebd85877c351daa0a069f1111a"}, {file = "brotlipy-0.7.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7e31f7adcc5851ca06134705fcf3478210da45d35ad75ec181e1ce9ce345bb38"}, {file = "brotlipy-0.7.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9448227b0df082e574c45c983fa5cd4bda7bfb11ea6b59def0940c1647be0c3c"}, @@ -1304,7 +1255,6 @@ brotlipy = [ {file = "brotlipy-0.7.0-cp36-cp36m-win32.whl", hash = "sha256:2e5c64522364a9ebcdf47c5744a5ddeb3f934742d31e61ebfbbc095460b47162"}, {file = "brotlipy-0.7.0-cp36-cp36m-win_amd64.whl", hash = "sha256:09ec3e125d16749b31c74f021aba809541b3564e5359f8c265cbae442810b41a"}, {file = "brotlipy-0.7.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:4e4638b49835d567d447a2cfacec109f9a777f219f071312268b351b6839436d"}, - {file = "brotlipy-0.7.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5664fe14f3a613431db622172bad923096a303d3adce55536f4409c8e2eafba4"}, {file = "brotlipy-0.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1379347337dc3d20b2d61456d44ccce13e0625db2611c368023b4194d5e2477f"}, {file = "brotlipy-0.7.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:22a53ccebcce2425e19f99682c12be510bf27bd75c9b77a1720db63047a77554"}, {file = "brotlipy-0.7.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4bac11c1ffba9eaa2894ec958a44e7f17778b3303c2ee9f99c39fcc511c26668"}, @@ -1427,53 +1377,53 @@ coloredlogs = [ {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, ] coverage = [ - {file = "coverage-6.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:675adb3b3380967806b3cbb9c5b00ceb29b1c472692100a338730c1d3e59c8b9"}, - {file = "coverage-6.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95a58336aa111af54baa451c33266a8774780242cab3704b7698d5e514840758"}, - {file = "coverage-6.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0a595a781f8e186580ff8e3352dd4953b1944289bec7705377c80c7e36c4d6c"}, - {file = "coverage-6.1.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d3c5f49ce6af61154060640ad3b3281dbc46e2e0ef2fe78414d7f8a324f0b649"}, - {file = "coverage-6.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:310c40bed6b626fd1f463e5a83dba19a61c4eb74e1ac0d07d454ebbdf9047e9d"}, - {file = "coverage-6.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a4d48e42e17d3de212f9af44f81ab73b9378a4b2b8413fd708d0d9023f2bbde4"}, - {file = "coverage-6.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ffa545230ca2ad921ad066bf8fd627e7be43716b6e0fcf8e32af1b8188ccb0ab"}, - {file = "coverage-6.1.2-cp310-cp310-win32.whl", hash = "sha256:cd2d11a59afa5001ff28073ceca24ae4c506da4355aba30d1e7dd2bd0d2206dc"}, - {file = "coverage-6.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:96129e41405887a53a9cc564f960d7f853cc63d178f3a182fdd302e4cab2745b"}, - {file = "coverage-6.1.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1de9c6f5039ee2b1860b7bad2c7bc3651fbeb9368e4c4d93e98a76358cdcb052"}, - {file = "coverage-6.1.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:80cb70264e9a1d04b519cdba3cd0dc42847bf8e982a4d55c769b9b0ee7cdce1e"}, - {file = "coverage-6.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:ba6125d4e55c0b8e913dad27b22722eac7abdcb1f3eab1bd090eee9105660266"}, - {file = "coverage-6.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8492d37acdc07a6eac6489f6c1954026f2260a85a4c2bb1e343fe3d35f5ee21a"}, - {file = "coverage-6.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66af99c7f7b64d050d37e795baadf515b4561124f25aae6e1baa482438ecc388"}, - {file = "coverage-6.1.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ebcc03e1acef4ff44f37f3c61df478d6e469a573aa688e5a162f85d7e4c3860d"}, - {file = "coverage-6.1.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98d44a8136eebbf544ad91fef5bd2b20ef0c9b459c65a833c923d9aa4546b204"}, - {file = "coverage-6.1.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:c18725f3cffe96732ef96f3de1939d81215fd6d7d64900dcc4acfe514ea4fcbf"}, - {file = "coverage-6.1.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c8e9c4bcaaaa932be581b3d8b88b677489975f845f7714efc8cce77568b6711c"}, - {file = "coverage-6.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:06d009e8a29483cbc0520665bc46035ffe9ae0e7484a49f9782c2a716e37d0a0"}, - {file = "coverage-6.1.2-cp36-cp36m-win32.whl", hash = "sha256:e5432d9c329b11c27be45ee5f62cf20a33065d482c8dec1941d6670622a6fb8f"}, - {file = "coverage-6.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:82fdcb64bf08aa5db881db061d96db102c77397a570fbc112e21c48a4d9cb31b"}, - {file = "coverage-6.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:94f558f8555e79c48c422045f252ef41eb43becdd945e9c775b45ebfc0cbd78f"}, - {file = "coverage-6.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:046647b96969fda1ae0605f61288635209dd69dcd27ba3ec0bf5148bc157f954"}, - {file = "coverage-6.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cc799916b618ec9fd00135e576424165691fec4f70d7dc12cfaef09268a2478c"}, - {file = "coverage-6.1.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:62646d98cf0381ffda301a816d6ac6c35fc97aa81b09c4c52d66a15c4bef9d7c"}, - {file = "coverage-6.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:27a3df08a855522dfef8b8635f58bab81341b2fb5f447819bc252da3aa4cf44c"}, - {file = "coverage-6.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:610c0ba11da8de3a753dc4b1f71894f9f9debfdde6559599f303286e70aeb0c2"}, - {file = "coverage-6.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:35b246ae3a2c042dc8f410c94bcb9754b18179cdb81ff9477a9089dbc9ecc186"}, - {file = "coverage-6.1.2-cp37-cp37m-win32.whl", hash = "sha256:0cde7d9fe2fb55ff68ebe7fb319ef188e9b88e0a3d1c9c5db7dd829cd93d2193"}, - {file = "coverage-6.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:958ac66272ff20e63d818627216e3d7412fdf68a2d25787b89a5c6f1eb7fdd93"}, - {file = "coverage-6.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a300b39c3d5905686c75a369d2a66e68fd01472ea42e16b38c948bd02b29e5bd"}, - {file = "coverage-6.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d3855d5d26292539861f5ced2ed042fc2aa33a12f80e487053aed3bcb6ced13"}, - {file = "coverage-6.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:586d38dfc7da4a87f5816b203ff06dd7c1bb5b16211ccaa0e9788a8da2b93696"}, - {file = "coverage-6.1.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a34fccb45f7b2d890183a263578d60a392a1a218fdc12f5bce1477a6a68d4373"}, - {file = "coverage-6.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bc1ee1318f703bc6c971da700d74466e9b86e0c443eb85983fb2a1bd20447263"}, - {file = "coverage-6.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3f546f48d5d80a90a266769aa613bc0719cb3e9c2ef3529d53f463996dd15a9d"}, - {file = "coverage-6.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd92ece726055e80d4e3f01fff3b91f54b18c9c357c48fcf6119e87e2461a091"}, - {file = "coverage-6.1.2-cp38-cp38-win32.whl", hash = "sha256:24ed38ec86754c4d5a706fbd5b52b057c3df87901a8610d7e5642a08ec07087e"}, - {file = "coverage-6.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:97ef6e9119bd39d60ef7b9cd5deea2b34869c9f0b9777450a7e3759c1ab09b9b"}, - {file = "coverage-6.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e5a8c947a2a89c56655ecbb789458a3a8e3b0cbf4c04250331df8f647b3de59"}, - {file = "coverage-6.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a39590d1e6acf6a3c435c5d233f72f5d43b585f5be834cff1f21fec4afda225"}, - {file = "coverage-6.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9d2c2e3ce7b8cc932a2f918186964bd44de8c84e2f9ef72dc616f5bb8be22e71"}, - {file = "coverage-6.1.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3348865798c077c695cae00da0924136bb5cc501f236cfd6b6d9f7a3c94e0ec4"}, - {file = "coverage-6.1.2-cp39-cp39-win32.whl", hash = "sha256:fae3fe111670e51f1ebbc475823899524e3459ea2db2cb88279bbfb2a0b8a3de"}, - {file = "coverage-6.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:af45eea024c0e3a25462fade161afab4f0d9d9e0d5a5d53e86149f74f0a35ecc"}, - {file = "coverage-6.1.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:eab14fdd410500dae50fd14ccc332e65543e7b39f6fc076fe90603a0e5d2f929"}, - {file = "coverage-6.1.2.tar.gz", hash = "sha256:d9a635114b88c0ab462e0355472d00a180a5fbfd8511e7f18e4ac32652e7d972"}, + {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, + {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, + {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, + {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, + {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, + {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, + {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, + {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, + {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, + {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, + {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, + {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, + {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, + {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, + {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, + {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, + {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, + {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, + {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, + {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, ] coveralls = [ {file = "coveralls-3.3.1-py2.py3-none-any.whl", hash = "sha256:f42015f31d386b351d4226389b387ae173207058832fbf5c8ec4b40e27b16026"}, @@ -1534,14 +1484,6 @@ ghp-import = [ {file = "ghp-import-2.0.2.tar.gz", hash = "sha256:947b3771f11be850c852c64b561c600fdddf794bab363060854c1ee7ad05e071"}, {file = "ghp_import-2.0.2-py3-none-any.whl", hash = "sha256:5f8962b30b20652cdffa9c5a9812f7de6bcb56ec475acac579807719bf242c46"}, ] -gitdb = [ - {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, - {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, -] -gitpython = [ - {file = "GitPython-3.1.24-py3-none-any.whl", hash = "sha256:dc0a7f2f697657acc8d7f89033e8b1ea94dd90356b2983bca89dc8d2ab3cc647"}, - {file = "GitPython-3.1.24.tar.gz", hash = "sha256:df83fdf5e684fef7c6ee2c02fc68a5ceb7e7e759d08b694088d0cacb4eba59e5"}, -] humanfriendly = [ {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, @@ -2019,18 +1961,10 @@ six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -smmap = [ - {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, - {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, -] snowballstemmer = [ {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] -stevedore = [ - {file = "stevedore-3.5.0-py3-none-any.whl", hash = "sha256:a547de73308fd7e90075bb4d301405bebf705292fa90a90fc3bcf9133f58616c"}, - {file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"}, -] taskipy = [ {file = "taskipy-1.9.0-py3-none-any.whl", hash = "sha256:02bd2c51c7356ed3f7f8853210ada1cd2ab273e68359ee865021c3057eec6615"}, {file = "taskipy-1.9.0.tar.gz", hash = "sha256:449c160b557cdb1d9c17097a5ea4aa0cd5223723ddbaaa5d5032dd16274fb8f0"}, From 63da5f24840c9a6e0fe3f3433be3805c29c0621d Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sat, 27 Nov 2021 12:30:20 +0530 Subject: [PATCH 23/27] Add lock no update task (#133) --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d21ad2fc..c0cc2d74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,6 +90,7 @@ docs = { cmd = "mkdocs serve", help = "Run the docs on a local automatically rel export = { cmd = "python -m scripts.export_requirements", help = "Export the requirements from poetry.lock to requirements.txt" } flake8 = { cmd = "python -m flake8", help = "Lints code with flake8" } lint = { cmd = "pre-commit run --all-files", help = "Checks all files for CI errors" } +lock = { cmd = 'poetry lock --no-update && task export --docs', help = 'Relock the dependencies without updating them. Also runs the export scripts' } precommit = { cmd = "pre-commit install --install-hooks", help = "Installs the precommit hook" } report = { cmd = "coverage report", help = "Show coverage report from previously run tests." } scripts = { cmd = 'python -m scripts', help = 'Run the scripts wrapper cli.' } From 85d94f4c2c8ed6517834a6a560812432b5b360f9 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Wed, 8 Dec 2021 17:45:04 +0530 Subject: [PATCH 24/27] Remove star imports :D --- scripts/news/__main__.py | 16 ++++++++++++++-- scripts/news/utils.py | 4 +++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index 01e9002c..7ea5c123 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -1,12 +1,24 @@ +import os from collections import defaultdict from datetime import datetime, timezone +from pathlib import Path from typing import Optional import click from . import __version__ -from .constants import * -from .utils import * +from .constants import NEWS_NEXT, REPO_ROOT, SECTIONS, TEMPLATE, TEMPLATE_FILE_PATH +from .utils import ( + NotRequiredIf, + err, + get_metadata_from_news, + get_project_meta, + glob_fragments, + out, + render_fragments, + save_news_fragment, + validate_pull_request_number, +) @click.group(context_settings=dict(help_option_names=["-h", "--help"]), invoke_without_command=True) diff --git a/scripts/news/utils.py b/scripts/news/utils.py index a836882e..6e8cc9ae 100644 --- a/scripts/news/utils.py +++ b/scripts/news/utils.py @@ -1,8 +1,10 @@ import glob import itertools +import os import tempfile import textwrap from datetime import datetime, timezone +from pathlib import Path from typing import Any, Dict, Generator, List, Mapping, Optional, Tuple, Union import click @@ -11,7 +13,7 @@ from click import Context, Option, echo, style from jinja2 import Template -from .constants import * +from .constants import ERROR_MSG_PREFIX, NO_NEWS_PATH_ERROR, PR_ENDPOINT, REPO_ROOT __all__ = ( From 0631b3e87b5d59c8b20e42229605d60f608d67be Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Wed, 8 Dec 2021 17:47:28 +0530 Subject: [PATCH 25/27] Use GITHUB_TOKEN while running the check tool as it would be passed my workflow runner --- scripts/news/check_news_workflow/__main__.py | 4 ++++ tox.ini | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/news/check_news_workflow/__main__.py b/scripts/news/check_news_workflow/__main__.py index 0d56934a..13427f0e 100644 --- a/scripts/news/check_news_workflow/__main__.py +++ b/scripts/news/check_news_workflow/__main__.py @@ -1,6 +1,7 @@ import pathlib import re import sys +from os import environ from typing import Tuple import requests @@ -12,6 +13,9 @@ HEADERS = {"accept": "application/vnd.github.v3+json"} REPO_ORG_NAME = "discord-modmail/modmail" +if GITHUB_TOKEN := environ.get("GITHUB_TOKEN"): + HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" + FILENAME_RE = re.compile( r"\d{4}-\d{2}-\d{2}(?:-\d{2}-\d{2}-\d{2})?\." # match `yyyy-mm-dd` or `yyyy-m-d` diff --git a/tox.ini b/tox.ini index 70c6a70b..bc557a04 100644 --- a/tox.ini +++ b/tox.ini @@ -28,7 +28,6 @@ ignore= per-file-ignores= tests/*:,ANN,S101,F401 docs.py:B008 - scripts/news/*:F405,F403 [isort] profile=black From b8efd238625072a7ae448c66406fbbd1604344e0 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 14 Dec 2021 12:18:50 +0530 Subject: [PATCH 26/27] Migrate to new changelog format --- news/latest.md | 67 +++++++++++++++++++++++ news/next/feature/2021-12-14.pr-119.md | 2 + news/next/feature/2021-12-14.pr-121.md | 2 + news/next/feature/2021-12-14.pr-71.md | 2 + news/next/feature/2021-12-14.pr-89.md | 2 + news/next/improvement/2021-12-14.pr-78.md | 2 + 6 files changed, 77 insertions(+) create mode 100644 news/latest.md create mode 100644 news/next/feature/2021-12-14.pr-119.md create mode 100644 news/next/feature/2021-12-14.pr-121.md create mode 100644 news/next/feature/2021-12-14.pr-71.md create mode 100644 news/next/feature/2021-12-14.pr-89.md create mode 100644 news/next/improvement/2021-12-14.pr-78.md diff --git a/news/latest.md b/news/latest.md new file mode 100644 index 00000000..8c51c90e --- /dev/null +++ b/news/latest.md @@ -0,0 +1,67 @@ +## 0.2.0 (2021-09-29) + +--- + +### Features + +- Interaction Paginator that uses discord buttons (#50) +- docker-compose.yml (#13) + - Running the bot after configuring the env vars is now as simple as `docker-compose up` +- Automatic docker image creation: `ghcr.io/discord-modmail/modmail` (#19) +- Dockerfile support for all supported hosting providers. (#58) +- Errors no longer happen silently and notify the user when they make a mistake. (#77) + +### Improvements + +- Refactored bot creation and bot running (#56) + - Running the bot is still the same method, but it loads extensions and plugins now. + - `bot.start()` can also be used if already in a running event loop. Keep in mind using it will require + handling loop errors, as run() does this automatically. + +### Internal + +- Code style: two blank lines after imports instead of one. (#70) + + +## 0.1.0 (2021-08-13) + +--- + +### Feature + +- Bot modes to determine behavior. Multiple can be applied at once. (#43) + - `PROD`: the default mode, no dev extensions or dev plugins load + - `DEVELOP`: the bot developer mode, most useful for people adding features to modmail +- Enables the extension_manager extension. + - `PLUGIN_DEV`: the plugin developer mode, useful for enabling plugin specific features + - This is not used yet. +- Extension loading system (#43) + - scans for extensions in the `modmail/extensions` folder and loads them if they are of the right format. + - all extensions must be loadable as a module, which means they must have `__init__.py` files in their directories. +- Plugin loading system (#43) + - scans for plugins in the `modmail/plugins` folder and loads them. + - Unlike extensions, plugins and their respective folders do not need to have `__init__.py` files and are allowed to be symlinks. +- Extension management commands (#43) + - load, reload, unload, list, refresh commands for dealing with extensions + - Run the `ext` command for more details when bot is in `DEVELOP` mode. +- Plugin management commands (#43) + - load, reload, unload, list, refresh commands for dealing with plugins + - Run the `plugins` command for more details. +- Extension metadata (#43) + - used to determine if a cog should load or not depending on the bot mode +- Plugin helper file (#43) + - `modmail/plugin_helpers.py` contains several helpers for making plugins + - `PluginCog` + - `ModmailBot`, imported from `modmail.bot` + - `ModmailLogger`, imported from `modmail.log` +- Meta Cog (#43) + - **NOTE**: The commands in this cog are not stabilized yet and should not be relied upon. + - Prefix command for getting the set prefix. Most useful by mentioning the bot. + - Uptime command which tells the end user how long the bot has been online. + - Ping command to see the bot latency. +- Guide on how to contribute to modmail, see \[CONTRIBUTING.md\] +- Start a Changelog + +### Bugfix + +- Make the bot `http_session` within an event loop. diff --git a/news/next/feature/2021-12-14.pr-119.md b/news/next/feature/2021-12-14.pr-119.md new file mode 100644 index 00000000..c54c1615 --- /dev/null +++ b/news/next/feature/2021-12-14.pr-119.md @@ -0,0 +1,2 @@ + +Officially support python 3.10 diff --git a/news/next/feature/2021-12-14.pr-121.md b/news/next/feature/2021-12-14.pr-121.md new file mode 100644 index 00000000..f0806ca5 --- /dev/null +++ b/news/next/feature/2021-12-14.pr-121.md @@ -0,0 +1,2 @@ + +Officially support windows and macos diff --git a/news/next/feature/2021-12-14.pr-71.md b/news/next/feature/2021-12-14.pr-71.md new file mode 100644 index 00000000..a9e45a35 --- /dev/null +++ b/news/next/feature/2021-12-14.pr-71.md @@ -0,0 +1,2 @@ + +Added Dispatcher system, although it is not hooked into important features like thread creation yet. diff --git a/news/next/feature/2021-12-14.pr-89.md b/news/next/feature/2021-12-14.pr-89.md new file mode 100644 index 00000000..c2adfca8 --- /dev/null +++ b/news/next/feature/2021-12-14.pr-89.md @@ -0,0 +1,2 @@ + +Add a custom changelogger, inspired from the one at python/cpython diff --git a/news/next/improvement/2021-12-14.pr-78.md b/news/next/improvement/2021-12-14.pr-78.md new file mode 100644 index 00000000..b8baf4ab --- /dev/null +++ b/news/next/improvement/2021-12-14.pr-78.md @@ -0,0 +1,2 @@ + +Embedified the meta commands so they have a nicer UI From 2c76232edd9f03095cc5c44c34e12976b5fda575 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 14 Dec 2021 12:26:47 +0530 Subject: [PATCH 27/27] Make changelog append to latest and not separate per version --- scripts/news/__main__.py | 30 +++++++++++++++++++++--------- scripts/news/constants.py | 11 +++++++++++ scripts/news/template.md.jinja | 10 ++-------- scripts/news/utils.py | 25 +++++++++++++------------ 4 files changed, 47 insertions(+), 29 deletions(-) diff --git a/scripts/news/__main__.py b/scripts/news/__main__.py index 7ea5c123..afc7f589 100644 --- a/scripts/news/__main__.py +++ b/scripts/news/__main__.py @@ -7,12 +7,20 @@ import click from . import __version__ -from .constants import NEWS_NEXT, REPO_ROOT, SECTIONS, TEMPLATE, TEMPLATE_FILE_PATH +from .constants import ( + DOCS_CHANGELOG, + HEADER, + LATEST_CHANGELOG, + NEWS_NEXT, + SECTIONS, + TEMPLATE, + TEMPLATE_FILE_PATH, +) from .utils import ( NotRequiredIf, err, get_metadata_from_news, - get_project_meta, + get_project_version, glob_fragments, out, render_fragments, @@ -124,24 +132,28 @@ def cli_build_news(ctx: click.Context, edit: Optional[bool], keep: bool) -> None fragment["path"] = path file_metadata[news_type].append(fragment) - name, version = get_project_meta() version_news = render_fragments( sections=SECTIONS, template=TEMPLATE_FILE_PATH, metadata=file_metadata, wrap=True, - version_data=(name, version), + version=get_project_version(), date=date, ) - news_path = Path(REPO_ROOT, f"news/{version}.md") - with open(news_path, mode="w") as file: - file.write(version_news) + with open(LATEST_CHANGELOG, mode="r+") as file: + content = file.read() + file.seek(0, 0) + changelog_content = version_news + "\n" + content + file.write(changelog_content) + + with open(DOCS_CHANGELOG, mode="w+") as file: + file.write(HEADER + "\n" + changelog_content) - out(f"All done! ✨ 🍰 ✨ Created {name}-v{version} news at {news_path}") + out("All done! ✨ 🍰 ✨") if edit: - click.edit(filename=str(news_path)) + click.edit(filename=str(LATEST_CHANGELOG)) if not keep: for news_fragment in NEWS_NEXT.glob("*/*.md"): diff --git a/scripts/news/constants.py b/scripts/news/constants.py index e8342cc2..580ad3bc 100644 --- a/scripts/news/constants.py +++ b/scripts/news/constants.py @@ -13,6 +13,8 @@ TEMPLATE_FILE_PATH = Path(Path(__file__).parent, "template.md.jinja") REPO_ROOT = Path(modmail.__file__).parent.parent NEWS_NEXT = Path(REPO_ROOT, "news/next") +LATEST_CHANGELOG = Path(REPO_ROOT, "news/latest.md") +DOCS_CHANGELOG = Path(REPO_ROOT, "docs/changelog.md") ERROR_MSG_PREFIX = "Oh no! 💥 💔 💥" TEMPLATE = """ @@ -36,3 +38,12 @@ "breaking": "Breaking Changes", "internal": "Internal", } + +HEADER = """\ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +""" diff --git a/scripts/news/template.md.jinja b/scripts/news/template.md.jinja index feab6991..15923e1d 100644 --- a/scripts/news/template.md.jinja +++ b/scripts/news/template.md.jinja @@ -1,16 +1,10 @@ -{% if top_line %} -# {{ top_line }} -{% elif version_data.name %} -# {{ version_data.name }} {{ version_data.version }} ({{ version_data.date }}) -{% else %} -# {{ version_data.version }} ({{ version_data.date }}) -{% endif %} +## {{ version }} ({{ date }}) --- {% for section, fragments in metadata.items() %} ### {{ sections[section] }} {% for fragment in fragments %} -- {{ fragment["news_entry"].strip("\n") }} (#{{ fragment["gh_pr"].split("-")[1] }}) +- {{ fragment["news_entry"].strip("\n") }} ([{{ fragment["gh_pr"] }}]({{ fragment["url"] }})) {% endfor %} {% endfor %} diff --git a/scripts/news/utils.py b/scripts/news/utils.py index 6e8cc9ae..bda0fcf8 100644 --- a/scripts/news/utils.py +++ b/scripts/news/utils.py @@ -19,7 +19,7 @@ __all__ = ( "NotRequiredIf", "get_metadata_from_news", - "get_project_meta", + "get_project_version", "glob_fragments", "err", "out", @@ -115,7 +115,10 @@ def glob_fragments(version: str, sections: Dict[str, str]) -> List[str]: def get_metadata_from_news(path: Path) -> dict: """Get metadata information from a news entry.""" new_fragment_file = path.stem - date, gh_pr, increment = new_fragment_file.partition(".") + date, gh_pr, = new_fragment_file.split( + "." + )[:2] + gh_pr = gh_pr.split("-")[1] news_type = path.parent.name with open(path, "r", encoding="utf-8") as file: @@ -123,21 +126,19 @@ def get_metadata_from_news(path: Path) -> dict: return { "date": date, - "gh_pr": gh_pr, + "url": PR_ENDPOINT.format(number=gh_pr), + "gh_pr": f"GH-{gh_pr}", "news_type": news_type, - "increment": increment, "news_entry": news_entry, } -def get_project_meta() -> Tuple[str, str]: - """Get the project version and name from pyproject.toml file.""" +def get_project_version() -> str: + """Get the project version from pyproject.toml file.""" with open("pyproject.toml", "rb") as pyproject: file_contents = tomli.load(pyproject) - version = file_contents["tool"]["poetry"]["version"] - name = file_contents["tool"]["poetry"]["name"] - return name, version + return file_contents["tool"]["poetry"]["version"] def render_fragments( @@ -145,17 +146,17 @@ def render_fragments( template: Path, metadata: Dict[str, list], wrap: bool, - version_data: Tuple[str, str], + version: str, date: Union[str, datetime], ) -> str: """Render the fragments into a news file.""" with open(template, mode="r") as template_file: jinja_template = Template(template_file.read(), trim_blocks=True) - version_data = {"name": version_data[0], "version": version_data[1], "date": date} res = jinja_template.render( sections=sections.copy(), - version_data=version_data, + version=version, + date=date, metadata=metadata, )