From fbdaa6b65d4be01f06b6e1a364d55b062a8f5649 Mon Sep 17 00:00:00 2001 From: Stefan Tatschner Date: Thu, 19 Dec 2024 15:28:43 +0100 Subject: [PATCH] chore: Release preparation kitchen sink MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use justfile instead of Makefile, since the release script can be integrated natively: `just release patch`. Might be possible to even include this in pyproject.toml since `uv` will provide a task runner (https://github.com/astral-sh/uv/issues/5903) that might be based on just. - Adjust the linting checks in the CI to run against the whole project. - Use the just runner in CI to avoid differing invocations in the project. - Add commitizen for bumping the release. This was provided by poetry in the past. I make too many mistakes without a tool here. In the future, this will be covered by uv as well: https://github.com/astral-sh/uv/issues/6298 - next… let's try to release a dev release and let's have a look if the pipeline works correctly. --- .github/workflows/linters-win32.yml | 2 +- .github/workflows/linters.yml | 16 +-- .github/workflows/tests.yml | 4 +- CONTRIBUTING.md | 2 - Makefile | 58 ---------- justfile | 63 +++++++++++ pyproject.toml | 9 ++ scripts/gen_constants.py | 1 - scripts/release.py | 169 ---------------------------- uv.lock | 98 ++++++++++++++++ 10 files changed, 181 insertions(+), 241 deletions(-) delete mode 100644 Makefile create mode 100644 justfile delete mode 100755 scripts/release.py diff --git a/.github/workflows/linters-win32.yml b/.github/workflows/linters-win32.yml index db6abb9e2..4fe0819ac 100644 --- a/.github/workflows/linters-win32.yml +++ b/.github/workflows/linters-win32.yml @@ -46,4 +46,4 @@ jobs: - name: Run mypy run: | - uv run make lint-win32 + uv run just win32-lint-mypy diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 939fae0d0..44bcd0dd1 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -44,18 +44,18 @@ jobs: run: | uv sync --all-extras --dev - - name: Run ruff + - name: Run reuse lint run: | - uv run ruff check src tests + uv run just lint-reuse - - name: Run mypy + - name: Run ruff format run: | - uv run mypy src tests + uv run just lint-ruff-format - - name: Run ruff format + - name: Run ruff check run: | - uv run ruff format --check src tests + uv run just lint-ruff-check - - name: Run reuse lint + - name: Run mypy run: | - uv run reuse lint + uv run just lint-mypy diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e1c45c5f4..9895e8bcb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,7 @@ jobs: - name: Run pytest run: | - uv run make pytest + uv run just run-test-pytest bats: strategy: @@ -78,4 +78,4 @@ jobs: - name: Run bats run: | - uv run make bats + uv run just run-test-bats diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf6589f74..becc114c6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,8 +18,6 @@ Several linters and unit tests are used to catch programming errors and regressi The relevant tools and their versions are specified in the `pyproject.toml`. [Github Actions](https://docs.github.com/en/actions) are configured to run against all merge requests. -To run these checks locally, use `make lint` and `make test`. - ## Commit Messages We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for structured commit messages. diff --git a/Makefile b/Makefile deleted file mode 100644 index 051f8766a..000000000 --- a/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -# SPDX-FileCopyrightText: AISEC Pentesting Team -# -# SPDX-License-Identifier: CC0-1.0 - -.PHONY: default -default: - @echo "available targets:" - @echo "" - @echo " fmt run autoformatters" - @echo " lint run linters" - @echo " docs build docs" - @echo " tests run testsuite" - @echo " pytest run pytest tests" - @echo " bats run bats end to end tests" - @echo " constants generate transport constants for compat reasons" - @echo " clean delete build artifacts" - -.PHONY: lint -lint: - mypy --pretty src tests - ruff check - ruff format --check - find tests/bats \( -iname "*.bash" -or -iname "*.bats" -or -iname "*.sh" \) | xargs shellcheck - reuse lint - -.PHONY: lint-win32 -lint-win32: - mypy --platform win32 --exclude "gallia\/log\.py" --exclude "hr" src tests - ruff check src tests - -.PHONY: fmt -fmt: - ruff check --fix-only - ruff format - find tests/bats \( -iname "*.bash" -or -iname "*.bats" -or -iname "*.sh" \) | xargs shfmt -w - -.PHONY: docs -docs: - $(MAKE) -C docs html - -.PHONY: tests -tests: pytest bats - -.PHONY: pytest -pytest: - python -m pytest -v --cov=$(PWD) --cov-report html tests/pytest - -.PHONY: bats -bats: - ./tests/bats/run_bats.sh - -.PHONY: constants -constants: - ./scripts/gen_constants.py | ruff format - > src/gallia/transports/_can_constants.py - -.PHONY: clean -clean: - $(MAKE) -C docs clean diff --git a/justfile b/justfile new file mode 100644 index 000000000..33fc8916e --- /dev/null +++ b/justfile @@ -0,0 +1,63 @@ +# SPDX-FileCopyrightText: AISEC Pentesting Team +# +# SPDX-License-Identifier: CC0-1.0 + +default: + @just --list + +[private] +lint-mypy: + mypy --pretty src tests + +[private] +lint-ruff-check: + ruff check + +[private] +lint-ruff-format: + ruff format --check + +[private] +lint-shellcheck: + find tests/bats \( -iname "*.bash" -or -iname "*.bats" -or -iname "*.sh" \) | xargs shellcheck + +[private] +lint-reuse: + reuse lint + +lint: lint-mypy lint-ruff-check lint-ruff-format lint-ruff-format lint-shellcheck lint-reuse + +win32-lint-mypy: + mypy --platform win32 --exclude "gallia\/log\.py" --exclude "hr" src tests + +fmt: + ruff check --fix-only + ruff format + find tests/bats \( -iname "*.bash" -or -iname "*.bats" -or -iname "*.sh" \) | xargs shfmt -w + +run-tests: run-test-pytest run-test-bats + +run-test-pytest: + python -m pytest -v --cov={{justfile_directory()}} --cov-report html tests/pytest + +run-test-bats: + ./tests/bats/run_bats.sh + +gen-constants: && fmt + ./scripts/gen_constants.py > src/gallia/transports/_can_constants.py + +release increment: + cz bump --increment {{increment}} + git push --follow-tags + gh release create "v$(cz version -p)" + +pre-release increment premode: + cz bump --increment {{increment}} --prerelease {{premode}} + git push --follow-tags + gh release create --prerelease "v$(cz version -p)" + +make-docs: + make -C docs html + +clean: + make -C docs clean diff --git a/pyproject.toml b/pyproject.toml index 5c94ecdeb..8f803a15c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,7 @@ dependencies = [ [project.optional-dependencies] dev = [ "Sphinx >=8.0", + "commitizen>=4.1.0", "construct-typing >=0.5.2,<0.7.0", "mypy >=1.0,<2.0", "myst-parser >=3.0.1,<4.1", @@ -63,6 +64,7 @@ dev = [ "python-lsp-server >=1.5,<2.0", "reuse >=4.0,<5.0", "ruff >=0.8.0", + "rust-just>=1.38.0", "sphinx-rtd-theme >=3", "types-tabulate >=0.9,<0.10", ] @@ -129,3 +131,10 @@ asyncio_default_fixture_loop_scope = "function" filterwarnings = [ "error::UserWarning", ] + +[tool.commitizen] +name = "cz_conventional_commits" +tag_format = "v$version" +version_scheme = "pep440" +version_provider = "pep621" +annotated_tag = true diff --git a/scripts/gen_constants.py b/scripts/gen_constants.py index 247c237c8..23f99cc99 100755 --- a/scripts/gen_constants.py +++ b/scripts/gen_constants.py @@ -6,7 +6,6 @@ import socket - TEMPLATE = f"""# This file has been autogenerated by `make constants`. # !! DO NOT CHANGE MANUALLY !! diff --git a/scripts/release.py b/scripts/release.py deleted file mode 100755 index deaf214fe..000000000 --- a/scripts/release.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python3 - -# SPDX-FileCopyrightText: AISEC Pentesting Team -# -# SPDX-License-Identifier: Apache-2.0 - -import sys -from argparse import ArgumentParser, Namespace -from enum import Enum, auto, unique -from subprocess import run -from typing import Any, NoReturn - -DRY_RUN = False - - -@unique -class BumpMode(Enum): - PATCH = "patch" - MINOR = "minor" - MAJOR = "major" - PREPATCH = "prepatch" - PREMINOR = "preminor" - PREMAJOR = "premajor" - - -@unique -class ReleaseNotes(Enum): - INTERACTIVE = auto() - GENERATE = auto() - - -def die(msg: str) -> NoReturn: - print(msg) - sys.exit(1) - - -def run_wrapper(*args: Any, **kwargs: Any) -> Any: - if DRY_RUN: - return print(f"would run: {args} {kwargs}") - return run(*args, **kwargs, check=True) - - -def git_pull() -> None: - run_wrapper(["git", "pull"]) - - -def check_project(rule: BumpMode | str) -> None: - p = run( - ["git", "rev-parse", "--abbrev-ref", "HEAD"], - check=True, - capture_output=True, - ) - current_branch = p.stdout.decode().strip() - - if isinstance(rule, BumpMode): - match rule: - case BumpMode.PATCH | BumpMode.PREPATCH if not current_branch.endswith("-maint"): - die("minor or patch releases must be cut from maintenance branch!") - case ( - BumpMode.MAJOR - | BumpMode.PREMAJOR - | BumpMode.MINOR - | BumpMode.PREMINOR - ) if current_branch != "master": - die("major releases must be cut from master branch!") - p = run( - ["git", "diff", "--no-ext-diff", "--quiet", "--exit-code"], - check=True, - ) - if p.returncode != 0: - die("commit your changes first!") - - -def get_current_version() -> str: - p = run(["poetry", "version"], check=True, capture_output=True) - version_str = p.stdout.decode().strip() - return version_str.split(" ", 2)[1] - - -def bump_version(rule: BumpMode | str) -> None: - if isinstance(rule, BumpMode): - run(["poetry", "version", rule.value], check=True) - elif isinstance(rule, str): - run(["poetry", "version", rule], check=True) - else: - raise ValueError("BUG: wrong type") - - -def commit_bump(version: str) -> None: - run_wrapper( - ["git", "commit", "-a", "-m", f"chore: Bump v{version} release"], - ) - run_wrapper( - ["git", "tag", "-a", "-m", f"gallia v{version}", f"v{version}"], - ) - - -def github_release(version: str, rule: BumpMode | str, notes: ReleaseNotes) -> None: - run_wrapper(["git", "push", "--follow-tags"]) - - cmd = ["gh", "release", "create"] - match rule: - case BumpMode() if notes == ReleaseNotes.GENERATE: - cmd += ["--generate-notes"] - case BumpMode() if rule.value.startswith("pre"): - cmd += ["--p"] - # Force experiments to be --prerelease. - case str(): - cmd += ["--prerelease"] - - cmd += [f"v{version}"] - - run_wrapper(cmd) - - -def parse_args() -> Namespace: - parser = ArgumentParser() - parser.add_argument( - "-d", - "--dry-run", - action="store_true", - help="dry run, do not change anything", - ) - parser.add_argument( - "-g", - "--generate-notes", - action="store_true", - help="auto generate release notes", - ) - - group = parser.add_mutually_exclusive_group() - group.add_argument( - "--version", - help="set version manually", - ) - group.add_argument( - "--rule", - choices=list(map(lambda x: x.value, list(BumpMode))), # noqa: C417 - help="bumprule for the next version", - ) - args = parser.parse_args() - return args - - -def main() -> None: - args = parse_args() - if args.dry_run: - global DRY_RUN # noqa: PLW0603 - DRY_RUN = True - - rule = BumpMode(args.rule) if args.rule else args.version - notes = ReleaseNotes.GENERATE if args.generate_notes else ReleaseNotes.INTERACTIVE - - check_project(rule) - git_pull() - - bump_version(rule) - new_version = get_current_version() - - commit_bump(new_version) - github_release(new_version, rule, notes) - git_pull() - - -if __name__ == "__main__": - try: - main() - except KeyboardInterrupt: - pass diff --git a/uv.lock b/uv.lock index 181c4591e..c80a348f4 100644 --- a/uv.lock +++ b/uv.lock @@ -187,6 +187,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] +[[package]] +name = "commitizen" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argcomplete" }, + { name = "charset-normalizer" }, + { name = "colorama" }, + { name = "decli" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "questionary" }, + { name = "termcolor" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/c5/66f1b977b48501a33f5fd33253aba14786483b08aba987718d272e99e732/commitizen-4.1.0.tar.gz", hash = "sha256:4f2d9400ec411aec1c738d4c63fc7fd5807cd6ddf6be970869e03e68b88ff718", size = 51252 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/f7/7f70adfbf3553ffdbe391eaacde72b21dbc1b4226ae56ca32e8ded1bf70b/commitizen-4.1.0-py3-none-any.whl", hash = "sha256:2e6c5fbd442cab4bcc5a04bc86ef2196ef84bcf611317d6c596e87f5bb4c09f5", size = 72282 }, +] + [[package]] name = "construct" version = "2.10.68" @@ -244,6 +265,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/26/74/b0729f196f328ac55e42b1e22ec2f16d8bcafe4b8158a26ec9f1cdd1d93e/coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611", size = 211815 }, ] +[[package]] +name = "decli" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/a0/a4658f93ecb589f479037b164dc13c68d108b50bf6594e54c820749f97ac/decli-0.6.2.tar.gz", hash = "sha256:36f71eb55fd0093895efb4f416ec32b7f6e00147dda448e3365cf73ceab42d6f", size = 7424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/70/3ea48dc9e958d7d66c44c9944809181f1ca79aaef25703c023b5092d34ff/decli-0.6.2-py3-none-any.whl", hash = "sha256:2fc84106ce9a8f523ed501ca543bdb7e416c064917c12a59ebdc7f311a97b7ed", size = 7854 }, +] + [[package]] name = "docstring-to-markdown" version = "0.15" @@ -273,12 +303,14 @@ dependencies = [ { name = "construct" }, { name = "platformdirs" }, { name = "pydantic" }, + { name = "rust-just" }, { name = "tabulate" }, { name = "zstandard" }, ] [package.optional-dependencies] dev = [ + { name = "commitizen" }, { name = "construct-typing" }, { name = "mypy" }, { name = "myst-parser" }, @@ -300,6 +332,7 @@ requires-dist = [ { name = "aiosqlite", specifier = ">=0.18" }, { name = "argcomplete", specifier = ">=2,<4" }, { name = "boltons", specifier = ">=24.1.0" }, + { name = "commitizen", marker = "extra == 'dev'", specifier = ">=4.1.0" }, { name = "construct", specifier = ">=2.10,<3.0" }, { name = "construct-typing", marker = "extra == 'dev'", specifier = ">=0.5.2,<0.7.0" }, { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.0,<2.0" }, @@ -314,6 +347,7 @@ requires-dist = [ { name = "python-lsp-server", marker = "extra == 'dev'", specifier = ">=1.5,<2.0" }, { name = "reuse", marker = "extra == 'dev'", specifier = ">=4.0,<5.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.8.0" }, + { name = "rust-just", specifier = ">=1.38.0" }, { name = "sphinx", marker = "extra == 'dev'", specifier = ">=8.0" }, { name = "sphinx-rtd-theme", marker = "extra == 'dev'", specifier = ">=3" }, { name = "tabulate", specifier = ">=0.9" }, @@ -540,6 +574,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, ] +[[package]] +name = "prompt-toolkit" +version = "3.0.36" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/93/180be2342f89f16543ec4eb3f25083b5b84eba5378f68efff05409fb39a9/prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63", size = 423863 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/37/791f1a6edd13c61cac85282368aa68cb0f3f164440fdf60032f2cc6ca34e/prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305", size = 386414 }, +] + [[package]] name = "pycparser" version = "2.22" @@ -760,6 +806,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, ] +[[package]] +name = "questionary" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "prompt-toolkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/d0/d73525aeba800df7030ac187d09c59dc40df1c878b4fab8669bdc805535d/questionary-2.0.1.tar.gz", hash = "sha256:bcce898bf3dbb446ff62830c86c5c6fb9a22a54146f0f5597d3da43b10d8fc8b", size = 24726 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/e7/2dd8f59d1d328773505f78b85405ddb1cfe74126425d076ce72e65540b8b/questionary-2.0.1-py3-none-any.whl", hash = "sha256:8ab9a01d0b91b68444dff7f6652c1e754105533f083cbe27597c8110ecc230a2", size = 34248 }, +] + [[package]] name = "requests" version = "2.32.3" @@ -827,6 +885,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/03/8f/e4fa95288b81233356d9a9dcaed057e5b0adc6399aa8fd0f6d784041c9c3/ruff-0.8.3-py3-none-win_arm64.whl", hash = "sha256:fe2756edf68ea79707c8d68b78ca9a58ed9af22e430430491ee03e718b5e4936", size = 9078754 }, ] +[[package]] +name = "rust-just" +version = "1.38.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/5c/b043e498dc10fcbc02cd06e776ca9e3ef9d888afa8ed6963588964147f76/rust_just-1.38.0.tar.gz", hash = "sha256:05d092602075b4ef244dee4a94267f998a66523ca39b8ac8320630c82662dac9", size = 1387350 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/6b/5f416dfdee8ab1c3e73088dca4e10ac14956239f8c289f4f1b2ffda6511a/rust_just-1.38.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cae21f93bd10223b6e5b50be32aab2b449e46ce1cdf2c8e576b2d85a751fee79", size = 1758552 }, + { url = "https://files.pythonhosted.org/packages/53/45/fe3a456cd39e674c27149b907f802c89e21ad5fa8617178f5d765fbc6495/rust_just-1.38.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b64d750ded84f71fcd49271d88e162e0cbb111e9383b1dc9aa8b8a157e92fd06", size = 1628336 }, + { url = "https://files.pythonhosted.org/packages/c2/46/a4fa7b053af1c80942049f2f22b8a91298776292cdb0cbd2d66b2faa9560/rust_just-1.38.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:443548c03f1dcf46b82347c10f2ef3faf75cf6bee1fc382f9b6bea2b92c59516", size = 1754360 }, + { url = "https://files.pythonhosted.org/packages/44/16/b53cc27a514bd60928338fc61437bf5e6b45d80a4ba88321f882f355e155/rust_just-1.38.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:573ba5bcf94f446ef0e269ab99e3d2de201193dc41906339ca346cbc9ef336fd", size = 1770210 }, + { url = "https://files.pythonhosted.org/packages/45/ea/98235bb6d63ae11fba81e6f1d936b3e54e14d4fbf3430d9598b1407942c1/rust_just-1.38.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:744e2fe7a33f5cbf02a04bda7c07d9e0e4e924c7a47e35096cb034a45609b18a", size = 1893101 }, + { url = "https://files.pythonhosted.org/packages/72/a7/5ee28651daf9c0f214cf9d0f1d90e5c78e1b2561c12a8a716431081a52be/rust_just-1.38.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a1687535ce940149b825ca12a6afe485fcbd8d55766e5e18fef95a4c820c189", size = 1947836 }, + { url = "https://files.pythonhosted.org/packages/2a/96/f352440ccb18a2848bd5aacd309f80fc63b42413a13a651885d248500efd/rust_just-1.38.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7bd519d9d03460b6c32b5d2a082af5080a63e836e8e291c875a62ce3fb2c187", size = 2423371 }, + { url = "https://files.pythonhosted.org/packages/9c/bc/df996131791814f476f10441a5122fa28d8849dc3d77ceaea8ede0ea4eda/rust_just-1.38.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d0018f1cf151161307e7c3f340da290819fc2c5cf240c20a0f6b4de71884a14", size = 1877732 }, + { url = "https://files.pythonhosted.org/packages/ca/25/c6290d129faae0c872869a119e7946e4ae8738b2aa073e40734b5001ade5/rust_just-1.38.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b71eae6d358d0d7fb10f9ba7add658da0a3fc00d9e4d1473741901a0c8920450", size = 1769910 }, + { url = "https://files.pythonhosted.org/packages/f8/a3/6640b29f0bb080c3ffcfe10c7a5e9ba05dd244d0d030204079ca650f1443/rust_just-1.38.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5c0dd27a3616745d5d30e7dc59b1f539c95821da0b3a27bae1837a6202ea0bd4", size = 1799174 }, + { url = "https://files.pythonhosted.org/packages/92/8c/df133ec92d583232a3a78030eb538b093d51a8c9b4b8fd4054a1015e2411/rust_just-1.38.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ac4965aefc741faaa3106b6c8d6231353e067ce93582677cebc338ae25637430", size = 1887808 }, + { url = "https://files.pythonhosted.org/packages/3a/e8/4ba3b2405db0864f9ea5cee2aaa7c057d5ee6c122e996428e9c2b749ddf0/rust_just-1.38.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c626588d64b5fd39bbe5072c6ddd0e0f443b1682a4143be45429046f42be66e3", size = 1938453 }, + { url = "https://files.pythonhosted.org/packages/23/17/0f10aca56132d47e085c33a46d4a016a08ec081cd9efb8e55953ddc2067e/rust_just-1.38.0-py3-none-win32.whl", hash = "sha256:bcbaa423988d94218a457f44aa1310e959a2e634a93147d70cb9940b6a687a5d", size = 1559911 }, + { url = "https://files.pythonhosted.org/packages/a1/78/0a8b305c9fcd16a639ad6dd56268c642828769cd736041d554c82dc135ea/rust_just-1.38.0-py3-none-win_amd64.whl", hash = "sha256:2590b0a3eed6090129a618b7cbd94e107407b618eef95baba8c596fcf598f7a7", size = 1679295 }, +] + [[package]] name = "snowballstemmer" version = "2.2.0" @@ -952,6 +1032,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, ] +[[package]] +name = "termcolor" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/37/72/88311445fd44c455c7d553e61f95412cf89054308a1aa2434ab835075fc5/termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f", size = 13057 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/be/df630c387a0a054815d60be6a97eb4e8f17385d5d6fe660e1c02750062b4/termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8", size = 7755 }, +] + [[package]] name = "tomlkit" version = "0.13.2" @@ -1016,6 +1105,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, ] +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +] + [[package]] name = "zstandard" version = "0.23.0"