diff --git a/generated_templates/piptools-python/your_cool_package/.github/workflows/tests.yml b/generated_templates/piptools-python/your_cool_package/.github/workflows/tests.yml index 3d22a91..034e980 100644 --- a/generated_templates/piptools-python/your_cool_package/.github/workflows/tests.yml +++ b/generated_templates/piptools-python/your_cool_package/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.12", "3.11"] + python-version: ["3.13", "3.12", "3.11"] steps: - name: Checkout run: | diff --git a/generated_templates/piptools-python/your_cool_package/.gitignore b/generated_templates/piptools-python/your_cool_package/.gitignore index c6aad5a..ec72297 100644 --- a/generated_templates/piptools-python/your_cool_package/.gitignore +++ b/generated_templates/piptools-python/your_cool_package/.gitignore @@ -10,4 +10,6 @@ __pycache__ !.editorconfig !.flake8 !.gitignore +!.pre-commit-config.yaml +!.pre-commit-hooks.yaml !.gitkeep diff --git a/generated_templates/piptools-python/your_cool_package/.pre-commit-config.yaml b/generated_templates/piptools-python/your_cool_package/.pre-commit-config.yaml new file mode 100644 index 0000000..61dd4fd --- /dev/null +++ b/generated_templates/piptools-python/your_cool_package/.pre-commit-config.yaml @@ -0,0 +1,12 @@ +# pre-commit plugin configuration +# See https://pre-commit.com for more information +default_install_hook_types: + - pre-commit + - post-rewrite + - pre-push + +repos: + - repo: https://github.com/jedie/cli-base-utilities + rev: v0.11.0 + hooks: + - id: update-readme-history diff --git a/generated_templates/piptools-python/your_cool_package/.pre-commit-hooks.yaml b/generated_templates/piptools-python/your_cool_package/.pre-commit-hooks.yaml new file mode 100644 index 0000000..d6f5771 --- /dev/null +++ b/generated_templates/piptools-python/your_cool_package/.pre-commit-hooks.yaml @@ -0,0 +1,13 @@ +# https://pre-commit.com/#creating-new-hooks +- id: update-readme-history + name: cli-base-utilities + description: >- + Update history in README.md from git log. + entry: "python -m cli_base update-readme-history -v" + language: python + language_version: python3 + require_serial: true + pass_filenames: false + always_run: true + verbose: true + stages: [pre-commit, post-rewrite, pre-push] diff --git a/generated_templates/piptools-python/your_cool_package/README.md b/generated_templates/piptools-python/your_cool_package/README.md index 7e1b16a..5a1f41d 100644 --- a/generated_templates/piptools-python/your_cool_package/README.md +++ b/generated_templates/piptools-python/your_cool_package/README.md @@ -7,3 +7,56 @@ [![License GPL-3.0-or-later](https://img.shields.io/pypi/l/your_cool_package)](https://github.com/john-doh/your_cool_package/blob/main/LICENSE) A minimal Python package + +## CLI + +[comment]: <> (✂✂✂ auto generated main help start ✂✂✂) +``` +Usage: ./cli.py [OPTIONS] COMMAND [ARGS]... + +╭─ Options ────────────────────────────────────────────────────────────────────────────────────────╮ +│ --help Show this message and exit. │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Commands ───────────────────────────────────────────────────────────────────────────────────────╮ +│ update-readme-history Update project history base on git commits/tags in README.md │ +│ version Print version and exit │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ +``` +[comment]: <> (✂✂✂ auto generated main help end ✂✂✂) + + +## dev CLI + +[comment]: <> (✂✂✂ auto generated dev help start ✂✂✂) +``` +Usage: ./dev-cli.py [OPTIONS] COMMAND [ARGS]... + +╭─ Options ────────────────────────────────────────────────────────────────────────────────────────╮ +│ --help Show this message and exit. │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Commands ───────────────────────────────────────────────────────────────────────────────────────╮ +│ check-code-style Check code style by calling darker + flake8 │ +│ coverage Run tests and show coverage report. │ +│ fix-code-style Fix code style of all your_cool_package source code files via darker │ +│ install Run pip-sync and install 'your_cool_package' via pip as editable. │ +│ mypy Run Mypy (configured in pyproject.toml) │ +│ pip-audit Run pip-audit check against current requirements files │ +│ publish Build and upload this project to PyPi │ +│ test Run unittests │ +│ tox Run tox │ +│ update Update "requirements*.txt" dependencies files │ +│ update-test-snapshot-files Update all test snapshot files (by remove and recreate all snapshot │ +│ files) │ +│ version Print version and exit │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ +``` +[comment]: <> (✂✂✂ auto generated dev help end ✂✂✂) + + +## History + +[comment]: <> (✂✂✂ auto generated history start ✂✂✂) + + + +[comment]: <> (✂✂✂ auto generated history end ✂✂✂) diff --git a/generated_templates/piptools-python/your_cool_package/cli.py b/generated_templates/piptools-python/your_cool_package/cli.py index 11341c5..56ce1c2 100755 --- a/generated_templates/piptools-python/your_cool_package/cli.py +++ b/generated_templates/piptools-python/your_cool_package/cli.py @@ -33,7 +33,7 @@ def print_no_pip_error(): sys.exit(-1) -assert sys.version_info >= (3, 9), f'Python version {sys.version_info} is too old!' +assert sys.version_info >= (3, 11), f'Python version {sys.version_info} is too old!' if sys.platform == 'win32': # wtf diff --git a/generated_templates/piptools-python/your_cool_package/dev-cli.py b/generated_templates/piptools-python/your_cool_package/dev-cli.py index 2f37f1a..eb90a53 100755 --- a/generated_templates/piptools-python/your_cool_package/dev-cli.py +++ b/generated_templates/piptools-python/your_cool_package/dev-cli.py @@ -33,7 +33,7 @@ def print_no_pip_error(): sys.exit(-1) -assert sys.version_info >= (3, 9), f'Python version {sys.version_info} is too old!' +assert sys.version_info >= (3, 11), f'Python version {sys.version_info} is too old!' if sys.platform == 'win32': # wtf @@ -105,6 +105,9 @@ def main(argv): verbose_check_call(PIP_PATH, 'install', '--no-deps', '-e', '.') store_dep_hash() + # Activate git pre-commit hooks: + verbose_check_call(PYTHON_PATH, '-m', 'pre_commit', 'install') + # Call our entry point CLI: try: verbose_check_call(PROJECT_SHELL_SCRIPT, *argv[1:]) diff --git a/generated_templates/piptools-python/your_cool_package/pyproject.toml b/generated_templates/piptools-python/your_cool_package/pyproject.toml index 097174b..c9be064 100644 --- a/generated_templates/piptools-python/your_cool_package/pyproject.toml +++ b/generated_templates/piptools-python/your_cool_package/pyproject.toml @@ -33,6 +33,7 @@ dev = [ "pip-audit", # https://github.com/pypa/pip-audit "mypy", # https://github.com/python/mypy "twine", # https://github.com/pypa/twine + "pre-commit", # https://github.com/pre-commit/pre-commit "typeguard", # https://github.com/agronholm/typeguard/ # https://github.com/akaihola/darker @@ -83,6 +84,10 @@ require_hashes=true ignore-vuln=[] +[tool.cli_base] +version_module_name = "your_cool_package" # Used by "update-readme-history" git hook + + [tool.darker] src = ['.'] revision = "origin/main..." @@ -133,7 +138,7 @@ exclude_lines = [ legacy_tox_ini = """ [tox] isolated_build = True -envlist = py{312,311} +envlist = py{313,312,311} skip_missing_interpreters = True [testenv] @@ -143,7 +148,7 @@ commands_pre = pip install -U pip-tools pip-sync requirements.dev.txt commands = - {envpython} -m coverage run --context='{envname}' + python3 -m coverage run --context='{envname}' """ diff --git a/generated_templates/piptools-python/your_cool_package/requirements.dev.txt b/generated_templates/piptools-python/your_cool_package/requirements.dev.txt index 03a3270..f71aadd 100644 --- a/generated_templates/piptools-python/your_cool_package/requirements.dev.txt +++ b/generated_templates/piptools-python/your_cool_package/requirements.dev.txt @@ -78,10 +78,10 @@ build==1.2.2 \ --hash=sha256:277ccc71619d98afdd841a0e96ac9fe1593b823af481d3b0cea748e8894e0613 \ --hash=sha256:45b7d306fed5299c934f42ead7fc470286a3a30c957e44246ad982ab285fbb43 # via pip-tools -bx-py-utils==102 \ - --hash=sha256:367d2bde9c7f2bf750f55748d4832daaf9df73e753b5efb351ec1a3899cc5d80 \ - --hash=sha256:6d131d40394b477de715169e80067a0ab4891c8f04afd33fbd7ca00e2faf21ae \ - --hash=sha256:961a0abf31b512f72c1473a4d115096b0c5becd32d08338ac62adbf5b217b680 +bx-py-utils==104 \ + --hash=sha256:508cfc1d0fa6c22298f697c4efaa913337847d488d8a53eeccfae9ee106123f6 \ + --hash=sha256:77fddff99aaf2cb558ce2974ef66d0e7a4d48cca1cbb99e43eca0d8b77fa07fc \ + --hash=sha256:c92ebc4fb122e3e3c228d984d0a1f5c3284c3da6aab1a1c753f7eb1f71bdab3a # via # cli-base-utilities # your_cool_package (pyproject.toml) @@ -153,6 +153,7 @@ cffi==1.17.1 \ --hash=sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662 \ --hash=sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3 \ --hash=sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff \ + --hash=sha256:da2e6428aa89f81b9a97c5818e7e5875f2c40bef6abbbe17e0d768199aa943f1 \ --hash=sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5 \ --hash=sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd \ --hash=sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f \ @@ -171,6 +172,11 @@ cffi==1.17.1 \ --hash=sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87 \ --hash=sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b # via cryptography +cfgv==3.4.0 \ + --hash=sha256:a26cde6f2dbc2fb41e75d59fe67f9a9deb9c0257659fd357194e97f3e0891297 \ + --hash=sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9 \ + --hash=sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560 + # via pre-commit chardet==5.2.0 \ --hash=sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7 \ --hash=sha256:5e60dfdc063399966f1864c5cfdb9dfeb45361dc57770ddf07eea37f6bea3019 \ @@ -487,6 +493,11 @@ html5lib==1.1 \ --hash=sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f \ --hash=sha256:e6a6ca5b1d876bce04ea36e2dd747a45b36fe3d9ac4db0b8437f46842fbebcbf # via pip-audit +identify==2.6.1 \ + --hash=sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0 \ + --hash=sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98 \ + --hash=sha256:fabaffe28c2f7e909ce6424a31ca8ee25b4c90beee6ca5900c5fdb77b2ef6aee + # via pre-commit idna==3.10 \ --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 @@ -538,10 +549,9 @@ license-expression==30.3.1 \ --hash=sha256:60d5bec1f3364c256a92b9a08583d7ea933c7aa272c8d36d04144a89a3858c01 \ --hash=sha256:97904b9185c7bbb1e98799606fa7424191c375e70ba63a524b6f7100e42ddc46 # via cyclonedx-python-lib -manageprojects==0.19.1 \ - --hash=sha256:7e0dc7df3d46e21bd9fd89c026c3d2980eb70563924d9b226ec04b850942f5bd \ - --hash=sha256:e251874464a2bb23e1c0508ce147e592787d88adb1df7850271a1ff6f0fe98a3 \ - --hash=sha256:ed33522b96ebe3cd67a4ec0a28e4673f4351dd235cf65c77ff4226845f39101e +manageprojects==0.19.2 \ + --hash=sha256:4c5649deb9791f3016c1848b34080e1564ea3bec2a8e25a09f9f0af9012086df \ + --hash=sha256:5f9b7d692df1540b8787ae31acc9613270be01c7d4416aad77e5dee4ca97e363 # via your_cool_package (pyproject.toml) markdown-it-py==3.0.0 \ --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \ @@ -756,6 +766,11 @@ nh3==0.2.18 \ --hash=sha256:de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307 \ --hash=sha256:f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe # via readme-renderer +nodeenv==1.9.1 \ + --hash=sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f \ + --hash=sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9 \ + --hash=sha256:e3d5a1b67a0cdf73b094aca8a68a80befbde9ca317303f18bf8af61198edc37f + # via pre-commit packageurl-python==0.15.6 \ --hash=sha256:0d39b14186da7367ab71ca35e656cfc1e81eaf98dcd2801ded953f34232bfc92 \ --hash=sha256:a40210652c89022772a6c8340d6066f7d5dc67132141e5284a4db7a27d0a8ab0 \ @@ -814,6 +829,11 @@ pluggy==1.5.0 \ --hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \ --hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 # via tox +pre-commit==3.8.0 \ + --hash=sha256:03a8cc30ccb2ff639d46e6f47563a1ceffb02578ce6ab3adb5f10c38d4e47cea \ + --hash=sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af \ + --hash=sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f + # via your_cool_package (pyproject.toml) py-serializable==1.1.1 \ --hash=sha256:008cf879c8a227851e745e307c1a1c9b58bc22ab87f3794a3750513b6c9e8713 \ --hash=sha256:f268db3afc42c8786da6dc64a8a36e33a82cf65cdcff22d1188b0927f6d4cfa9 @@ -936,7 +956,9 @@ pyyaml==6.0.2 \ --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 - # via cookiecutter + # via + # cookiecutter + # pre-commit readme-renderer==44.0 \ --hash=sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151 \ --hash=sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1 \ @@ -1063,7 +1085,9 @@ urllib3==2.2.3 \ virtualenv==20.26.5 \ --hash=sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6 \ --hash=sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4 - # via tox + # via + # pre-commit + # tox webencodings==0.5.1 \ --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \ --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923 diff --git a/generated_templates/piptools-python/your_cool_package/requirements.txt b/generated_templates/piptools-python/your_cool_package/requirements.txt index 7d89dbf..a73b3ed 100644 --- a/generated_templates/piptools-python/your_cool_package/requirements.txt +++ b/generated_templates/piptools-python/your_cool_package/requirements.txt @@ -11,10 +11,10 @@ async-timeout==4.0.3 \ --hash=sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028 \ --hash=sha256:fd893e4cb1d9dff66d0e0576cc6376a23eacbbcd43b6607e4d3ecfae1ab15367 # via cli-base-utilities -bx-py-utils==102 \ - --hash=sha256:367d2bde9c7f2bf750f55748d4832daaf9df73e753b5efb351ec1a3899cc5d80 \ - --hash=sha256:6d131d40394b477de715169e80067a0ab4891c8f04afd33fbd7ca00e2faf21ae \ - --hash=sha256:961a0abf31b512f72c1473a4d115096b0c5becd32d08338ac62adbf5b217b680 +bx-py-utils==104 \ + --hash=sha256:508cfc1d0fa6c22298f697c4efaa913337847d488d8a53eeccfae9ee106123f6 \ + --hash=sha256:77fddff99aaf2cb558ce2974ef66d0e7a4d48cca1cbb99e43eca0d8b77fa07fc \ + --hash=sha256:c92ebc4fb122e3e3c228d984d0a1f5c3284c3da6aab1a1c753f7eb1f71bdab3a # via # cli-base-utilities # your_cool_package (pyproject.toml) diff --git a/generated_templates/piptools-python/your_cool_package/your_cool_package/cli_dev/code_style.py b/generated_templates/piptools-python/your_cool_package/your_cool_package/cli_dev/code_style.py index 0d3a5ea..6e55ed8 100644 --- a/generated_templates/piptools-python/your_cool_package/your_cool_package/cli_dev/code_style.py +++ b/generated_templates/piptools-python/your_cool_package/your_cool_package/cli_dev/code_style.py @@ -2,6 +2,7 @@ from cli_base.cli_tools import code_style from cli_base.cli_tools.verbosity import OPTION_KWARGS_VERBOSE from cli_base.click_defaults import OPTION_ARGS_DEFAULT_TRUE + from your_cool_package.cli_dev import PACKAGE_ROOT, cli @@ -10,7 +11,7 @@ @click.option('-v', '--verbosity', **OPTION_KWARGS_VERBOSE) def fix_code_style(color: bool, verbosity: int): """ - Fix code style of all cli_base source code files via darker + Fix code style of all your_cool_package source code files via darker """ code_style.fix(package_root=PACKAGE_ROOT, darker_color=color, darker_verbose=verbosity > 0) diff --git a/generated_templates/piptools-python/your_cool_package/your_cool_package/cli_dev/packaging.py b/generated_templates/piptools-python/your_cool_package/your_cool_package/cli_dev/packaging.py index 0795e0f..8a0b6f4 100644 --- a/generated_templates/piptools-python/your_cool_package/your_cool_package/cli_dev/packaging.py +++ b/generated_templates/piptools-python/your_cool_package/your_cool_package/cli_dev/packaging.py @@ -1,20 +1,21 @@ import sys from pathlib import Path -import cli_base import click from cli_base.cli_tools.dev_tools import run_unittest_cli from cli_base.cli_tools.subprocess_utils import verbose_check_call from cli_base.cli_tools.verbosity import OPTION_KWARGS_VERBOSE from cli_base.run_pip_audit import run_pip_audit from manageprojects.utilities.publish import publish_package + +import your_cool_package from your_cool_package.cli_dev import PACKAGE_ROOT, cli @cli.command() def install(): """ - Run pip-sync and install 'cli_base' via pip as editable. + Run pip-sync and install 'your_cool_package' via pip as editable. """ verbose_check_call('pip-sync', PACKAGE_ROOT / 'requirements.dev.txt') verbose_check_call('pip', 'install', '--no-deps', '-e', '.') @@ -69,6 +70,9 @@ def update(): # Install new dependencies in current .venv: verbose_check_call(bin_path / 'pip-sync', 'requirements.dev.txt') + # Update git pre-commit hooks: + verbose_check_call(bin_path / 'pre_commit', 'autoupdate') + @cli.command() def publish(): @@ -77,8 +81,4 @@ def publish(): """ run_unittest_cli(verbose=False, exit_after_run=False) # Don't publish a broken state - publish_package( - module=cli_base, - package_path=PACKAGE_ROOT, - distribution_name='cli-base-utilities', - ) + publish_package(module=your_cool_package, package_path=PACKAGE_ROOT) diff --git a/generated_templates/piptools-python/your_cool_package/your_cool_package/tests/test_project_setup.py b/generated_templates/piptools-python/your_cool_package/your_cool_package/tests/test_project_setup.py index 7409c9d..a68f3a2 100644 --- a/generated_templates/piptools-python/your_cool_package/your_cool_package/tests/test_project_setup.py +++ b/generated_templates/piptools-python/your_cool_package/your_cool_package/tests/test_project_setup.py @@ -4,6 +4,7 @@ from bx_py_utils.path import assert_is_file from manageprojects.test_utils.project_setup import check_editor_config, get_py_max_line_length from cli_base.cli_tools.code_style import assert_code_style +from cli_base.cli_tools.subprocess_utils import ToolsExecutor from packaging.version import Version from your_cool_package import __version__ @@ -38,3 +39,8 @@ def test_check_editor_config(self): max_line_length = get_py_max_line_length(package_root=PACKAGE_ROOT) self.assertEqual(max_line_length, 119) + + def test_pre_commit_hooks(self): + executor = ToolsExecutor(cwd=PACKAGE_ROOT) + for command in ('migrate-config', 'validate-config', 'validate-manifest'): + executor.verbose_check_call('pre-commit', command, exit_on_error=True) diff --git a/generated_templates/piptools-python/your_cool_package/your_cool_package/tests/test_readme.py b/generated_templates/piptools-python/your_cool_package/your_cool_package/tests/test_readme.py new file mode 100644 index 0000000..213d134 --- /dev/null +++ b/generated_templates/piptools-python/your_cool_package/your_cool_package/tests/test_readme.py @@ -0,0 +1,49 @@ +from bx_py_utils.auto_doc import assert_readme_block +from bx_py_utils.path import assert_is_file +from manageprojects.test_utils.click_cli_utils import invoke_click +from manageprojects.tests.base import BaseTestCase + +from your_cool_package import constants +from your_cool_package.cli_app import cli +from your_cool_package.cli_dev import PACKAGE_ROOT +from your_cool_package.cli_dev import cli as dev_cli + + +def assert_cli_help_in_readme(text_block: str, marker: str): + README_PATH = PACKAGE_ROOT / 'README.md' + assert_is_file(README_PATH) + + text_block = text_block.replace(constants.CLI_EPILOG, '') + text_block = f'```\n{text_block.strip()}\n```' + assert_readme_block( + readme_path=README_PATH, + text_block=text_block, + start_marker_line=f'[comment]: <> (✂✂✂ auto generated {marker} start ✂✂✂)', + end_marker_line=f'[comment]: <> (✂✂✂ auto generated {marker} end ✂✂✂)', + ) + + +class ReadmeTestCase(BaseTestCase): + def test_main_help(self): + stdout = invoke_click(cli, '--help') + self.assert_in_content( + got=stdout, + parts=( + 'Usage: ./cli.py [OPTIONS] COMMAND [ARGS]...', + constants.CLI_EPILOG, + ), + ) + assert_cli_help_in_readme(text_block=stdout, marker='main help') + + def test_dev_help(self): + stdout = invoke_click(dev_cli, '--help') + self.assert_in_content( + got=stdout, + parts=( + 'Usage: ./dev-cli.py [OPTIONS] COMMAND [ARGS]...', + ' check-code-style ', + ' coverage ', + constants.CLI_EPILOG, + ), + ) + assert_cli_help_in_readme(text_block=stdout, marker='dev help') diff --git a/generated_templates/piptools-python/your_cool_package/your_cool_package/tests/test_readme_history.py b/generated_templates/piptools-python/your_cool_package/your_cool_package/tests/test_readme_history.py new file mode 100644 index 0000000..72f1bea --- /dev/null +++ b/generated_templates/piptools-python/your_cool_package/your_cool_package/tests/test_readme_history.py @@ -0,0 +1,29 @@ +import os +from unittest import TestCase, skipIf + +from bx_py_utils.auto_doc import assert_readme_block +from cli_base.cli_tools.git_history import get_git_history + +import your_cool_package +from your_cool_package.cli_dev import PACKAGE_ROOT + + +class ReadmeHistoryTestCase(TestCase): + @skipIf( + # After a release the history may be "changed" because of version bump + # and we should not block merge requests because of this. + 'GITHUB_ACTION' in os.environ, + reason='Skip on github actions', + ) + def test_readme_history(self): + git_history = get_git_history( + current_version=your_cool_package.__version__, + add_author=False, + ) + history = '\n'.join(git_history) + assert_readme_block( + readme_path=PACKAGE_ROOT / 'README.md', + text_block=f'\n{history}\n', + start_marker_line='[comment]: <> (✂✂✂ auto generated history start ✂✂✂)', + end_marker_line='[comment]: <> (✂✂✂ auto generated history end ✂✂✂)', + ) diff --git a/piptools-python/tests.py b/piptools-python/tests.py index 2ee27c5..a304486 100644 --- a/piptools-python/tests.py +++ b/piptools-python/tests.py @@ -64,7 +64,7 @@ def test_basic(self): self.assert_in('Code style: OK', output) output = self.test_project.check_output(dev_cli_bin, 'test') - self.assert_in('Ran 4 tests', output) + self.assert_in('Ran 8 tests', output) # The project unittests checks also the code style and tries to fix them, # in this case, we have a code difference: diff --git a/piptools-python/{{ cookiecutter.package_name }}/.github/workflows/tests.yml b/piptools-python/{{ cookiecutter.package_name }}/.github/workflows/tests.yml index ea590c8..534cc04 100644 --- a/piptools-python/{{ cookiecutter.package_name }}/.github/workflows/tests.yml +++ b/piptools-python/{{ cookiecutter.package_name }}/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.12", "3.11"] + python-version: ["3.13", "3.12", "3.11"] steps: - name: Checkout run: | diff --git a/piptools-python/{{ cookiecutter.package_name }}/.gitignore b/piptools-python/{{ cookiecutter.package_name }}/.gitignore index c6aad5a..ec72297 100644 --- a/piptools-python/{{ cookiecutter.package_name }}/.gitignore +++ b/piptools-python/{{ cookiecutter.package_name }}/.gitignore @@ -10,4 +10,6 @@ __pycache__ !.editorconfig !.flake8 !.gitignore +!.pre-commit-config.yaml +!.pre-commit-hooks.yaml !.gitkeep diff --git a/piptools-python/{{ cookiecutter.package_name }}/.pre-commit-config.yaml b/piptools-python/{{ cookiecutter.package_name }}/.pre-commit-config.yaml new file mode 100644 index 0000000..61dd4fd --- /dev/null +++ b/piptools-python/{{ cookiecutter.package_name }}/.pre-commit-config.yaml @@ -0,0 +1,12 @@ +# pre-commit plugin configuration +# See https://pre-commit.com for more information +default_install_hook_types: + - pre-commit + - post-rewrite + - pre-push + +repos: + - repo: https://github.com/jedie/cli-base-utilities + rev: v0.11.0 + hooks: + - id: update-readme-history diff --git a/piptools-python/{{ cookiecutter.package_name }}/.pre-commit-hooks.yaml b/piptools-python/{{ cookiecutter.package_name }}/.pre-commit-hooks.yaml new file mode 100644 index 0000000..d6f5771 --- /dev/null +++ b/piptools-python/{{ cookiecutter.package_name }}/.pre-commit-hooks.yaml @@ -0,0 +1,13 @@ +# https://pre-commit.com/#creating-new-hooks +- id: update-readme-history + name: cli-base-utilities + description: >- + Update history in README.md from git log. + entry: "python -m cli_base update-readme-history -v" + language: python + language_version: python3 + require_serial: true + pass_filenames: false + always_run: true + verbose: true + stages: [pre-commit, post-rewrite, pre-push] diff --git a/piptools-python/{{ cookiecutter.package_name }}/README.md b/piptools-python/{{ cookiecutter.package_name }}/README.md index 7cb88fd..912a785 100644 --- a/piptools-python/{{ cookiecutter.package_name }}/README.md +++ b/piptools-python/{{ cookiecutter.package_name }}/README.md @@ -7,3 +7,56 @@ [![License {{ cookiecutter.license }}](https://img.shields.io/pypi/l/{{ cookiecutter.package_name }})]({{ cookiecutter.package_url }}/blob/main/LICENSE) {{ cookiecutter.package_description }} + +## CLI + +[comment]: <> (✂✂✂ auto generated main help start ✂✂✂) +``` +Usage: ./cli.py [OPTIONS] COMMAND [ARGS]... + +╭─ Options ────────────────────────────────────────────────────────────────────────────────────────╮ +│ --help Show this message and exit. │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Commands ───────────────────────────────────────────────────────────────────────────────────────╮ +│ update-readme-history Update project history base on git commits/tags in README.md │ +│ version Print version and exit │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ +``` +[comment]: <> (✂✂✂ auto generated main help end ✂✂✂) + + +## dev CLI + +[comment]: <> (✂✂✂ auto generated dev help start ✂✂✂) +``` +Usage: ./dev-cli.py [OPTIONS] COMMAND [ARGS]... + +╭─ Options ────────────────────────────────────────────────────────────────────────────────────────╮ +│ --help Show this message and exit. │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Commands ───────────────────────────────────────────────────────────────────────────────────────╮ +│ check-code-style Check code style by calling darker + flake8 │ +│ coverage Run tests and show coverage report. │ +│ fix-code-style Fix code style of all your_cool_package source code files via darker │ +│ install Run pip-sync and install 'your_cool_package' via pip as editable. │ +│ mypy Run Mypy (configured in pyproject.toml) │ +│ pip-audit Run pip-audit check against current requirements files │ +│ publish Build and upload this project to PyPi │ +│ test Run unittests │ +│ tox Run tox │ +│ update Update "requirements*.txt" dependencies files │ +│ update-test-snapshot-files Update all test snapshot files (by remove and recreate all snapshot │ +│ files) │ +│ version Print version and exit │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ +``` +[comment]: <> (✂✂✂ auto generated dev help end ✂✂✂) + + +## History + +[comment]: <> (✂✂✂ auto generated history start ✂✂✂) + + + +[comment]: <> (✂✂✂ auto generated history end ✂✂✂) diff --git a/piptools-python/{{ cookiecutter.package_name }}/cli.py b/piptools-python/{{ cookiecutter.package_name }}/cli.py index 0340b03..04f0667 100755 --- a/piptools-python/{{ cookiecutter.package_name }}/cli.py +++ b/piptools-python/{{ cookiecutter.package_name }}/cli.py @@ -33,7 +33,7 @@ def print_no_pip_error(): sys.exit(-1) -assert sys.version_info >= (3, 9), f'Python version {sys.version_info} is too old!' +assert sys.version_info >= (3, 11), f'Python version {sys.version_info} is too old!' if sys.platform == 'win32': # wtf diff --git a/piptools-python/{{ cookiecutter.package_name }}/dev-cli.py b/piptools-python/{{ cookiecutter.package_name }}/dev-cli.py index c4c9931..d41c3fa 100755 --- a/piptools-python/{{ cookiecutter.package_name }}/dev-cli.py +++ b/piptools-python/{{ cookiecutter.package_name }}/dev-cli.py @@ -33,7 +33,7 @@ def print_no_pip_error(): sys.exit(-1) -assert sys.version_info >= (3, 9), f'Python version {sys.version_info} is too old!' +assert sys.version_info >= (3, 11), f'Python version {sys.version_info} is too old!' if sys.platform == 'win32': # wtf @@ -105,6 +105,9 @@ def main(argv): verbose_check_call(PIP_PATH, 'install', '--no-deps', '-e', '.') store_dep_hash() + # Activate git pre-commit hooks: + verbose_check_call(PYTHON_PATH, '-m', 'pre_commit', 'install') + # Call our entry point CLI: try: verbose_check_call(PROJECT_SHELL_SCRIPT, *argv[1:]) diff --git a/piptools-python/{{ cookiecutter.package_name }}/pyproject.toml b/piptools-python/{{ cookiecutter.package_name }}/pyproject.toml index 93c5ce8..7f038b4 100644 --- a/piptools-python/{{ cookiecutter.package_name }}/pyproject.toml +++ b/piptools-python/{{ cookiecutter.package_name }}/pyproject.toml @@ -33,6 +33,7 @@ dev = [ "pip-audit", # https://github.com/pypa/pip-audit "mypy", # https://github.com/python/mypy "twine", # https://github.com/pypa/twine + "pre-commit", # https://github.com/pre-commit/pre-commit "typeguard", # https://github.com/agronholm/typeguard/ # https://github.com/akaihola/darker @@ -83,6 +84,10 @@ require_hashes=true ignore-vuln=[] +[tool.cli_base] +version_module_name = "{{ cookiecutter.package_name }}" # Used by "update-readme-history" git hook + + [tool.darker] src = ['.'] revision = "origin/main..." @@ -133,7 +138,7 @@ exclude_lines = [ legacy_tox_ini = """ [tox] isolated_build = True -envlist = py{312,311} +envlist = py{313,312,311} skip_missing_interpreters = True [testenv] @@ -143,7 +148,7 @@ commands_pre = pip install -U pip-tools pip-sync requirements.dev.txt commands = - {envpython} -m coverage run --context='{envname}' + python3 -m coverage run --context='{envname}' """ diff --git a/piptools-python/{{ cookiecutter.package_name }}/requirements.dev.txt b/piptools-python/{{ cookiecutter.package_name }}/requirements.dev.txt index 03a3270..f71aadd 100644 --- a/piptools-python/{{ cookiecutter.package_name }}/requirements.dev.txt +++ b/piptools-python/{{ cookiecutter.package_name }}/requirements.dev.txt @@ -78,10 +78,10 @@ build==1.2.2 \ --hash=sha256:277ccc71619d98afdd841a0e96ac9fe1593b823af481d3b0cea748e8894e0613 \ --hash=sha256:45b7d306fed5299c934f42ead7fc470286a3a30c957e44246ad982ab285fbb43 # via pip-tools -bx-py-utils==102 \ - --hash=sha256:367d2bde9c7f2bf750f55748d4832daaf9df73e753b5efb351ec1a3899cc5d80 \ - --hash=sha256:6d131d40394b477de715169e80067a0ab4891c8f04afd33fbd7ca00e2faf21ae \ - --hash=sha256:961a0abf31b512f72c1473a4d115096b0c5becd32d08338ac62adbf5b217b680 +bx-py-utils==104 \ + --hash=sha256:508cfc1d0fa6c22298f697c4efaa913337847d488d8a53eeccfae9ee106123f6 \ + --hash=sha256:77fddff99aaf2cb558ce2974ef66d0e7a4d48cca1cbb99e43eca0d8b77fa07fc \ + --hash=sha256:c92ebc4fb122e3e3c228d984d0a1f5c3284c3da6aab1a1c753f7eb1f71bdab3a # via # cli-base-utilities # your_cool_package (pyproject.toml) @@ -153,6 +153,7 @@ cffi==1.17.1 \ --hash=sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662 \ --hash=sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3 \ --hash=sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff \ + --hash=sha256:da2e6428aa89f81b9a97c5818e7e5875f2c40bef6abbbe17e0d768199aa943f1 \ --hash=sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5 \ --hash=sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd \ --hash=sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f \ @@ -171,6 +172,11 @@ cffi==1.17.1 \ --hash=sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87 \ --hash=sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b # via cryptography +cfgv==3.4.0 \ + --hash=sha256:a26cde6f2dbc2fb41e75d59fe67f9a9deb9c0257659fd357194e97f3e0891297 \ + --hash=sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9 \ + --hash=sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560 + # via pre-commit chardet==5.2.0 \ --hash=sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7 \ --hash=sha256:5e60dfdc063399966f1864c5cfdb9dfeb45361dc57770ddf07eea37f6bea3019 \ @@ -487,6 +493,11 @@ html5lib==1.1 \ --hash=sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f \ --hash=sha256:e6a6ca5b1d876bce04ea36e2dd747a45b36fe3d9ac4db0b8437f46842fbebcbf # via pip-audit +identify==2.6.1 \ + --hash=sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0 \ + --hash=sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98 \ + --hash=sha256:fabaffe28c2f7e909ce6424a31ca8ee25b4c90beee6ca5900c5fdb77b2ef6aee + # via pre-commit idna==3.10 \ --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 @@ -538,10 +549,9 @@ license-expression==30.3.1 \ --hash=sha256:60d5bec1f3364c256a92b9a08583d7ea933c7aa272c8d36d04144a89a3858c01 \ --hash=sha256:97904b9185c7bbb1e98799606fa7424191c375e70ba63a524b6f7100e42ddc46 # via cyclonedx-python-lib -manageprojects==0.19.1 \ - --hash=sha256:7e0dc7df3d46e21bd9fd89c026c3d2980eb70563924d9b226ec04b850942f5bd \ - --hash=sha256:e251874464a2bb23e1c0508ce147e592787d88adb1df7850271a1ff6f0fe98a3 \ - --hash=sha256:ed33522b96ebe3cd67a4ec0a28e4673f4351dd235cf65c77ff4226845f39101e +manageprojects==0.19.2 \ + --hash=sha256:4c5649deb9791f3016c1848b34080e1564ea3bec2a8e25a09f9f0af9012086df \ + --hash=sha256:5f9b7d692df1540b8787ae31acc9613270be01c7d4416aad77e5dee4ca97e363 # via your_cool_package (pyproject.toml) markdown-it-py==3.0.0 \ --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \ @@ -756,6 +766,11 @@ nh3==0.2.18 \ --hash=sha256:de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307 \ --hash=sha256:f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe # via readme-renderer +nodeenv==1.9.1 \ + --hash=sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f \ + --hash=sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9 \ + --hash=sha256:e3d5a1b67a0cdf73b094aca8a68a80befbde9ca317303f18bf8af61198edc37f + # via pre-commit packageurl-python==0.15.6 \ --hash=sha256:0d39b14186da7367ab71ca35e656cfc1e81eaf98dcd2801ded953f34232bfc92 \ --hash=sha256:a40210652c89022772a6c8340d6066f7d5dc67132141e5284a4db7a27d0a8ab0 \ @@ -814,6 +829,11 @@ pluggy==1.5.0 \ --hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \ --hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 # via tox +pre-commit==3.8.0 \ + --hash=sha256:03a8cc30ccb2ff639d46e6f47563a1ceffb02578ce6ab3adb5f10c38d4e47cea \ + --hash=sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af \ + --hash=sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f + # via your_cool_package (pyproject.toml) py-serializable==1.1.1 \ --hash=sha256:008cf879c8a227851e745e307c1a1c9b58bc22ab87f3794a3750513b6c9e8713 \ --hash=sha256:f268db3afc42c8786da6dc64a8a36e33a82cf65cdcff22d1188b0927f6d4cfa9 @@ -936,7 +956,9 @@ pyyaml==6.0.2 \ --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 - # via cookiecutter + # via + # cookiecutter + # pre-commit readme-renderer==44.0 \ --hash=sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151 \ --hash=sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1 \ @@ -1063,7 +1085,9 @@ urllib3==2.2.3 \ virtualenv==20.26.5 \ --hash=sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6 \ --hash=sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4 - # via tox + # via + # pre-commit + # tox webencodings==0.5.1 \ --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \ --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923 diff --git a/piptools-python/{{ cookiecutter.package_name }}/requirements.txt b/piptools-python/{{ cookiecutter.package_name }}/requirements.txt index 7d89dbf..a73b3ed 100644 --- a/piptools-python/{{ cookiecutter.package_name }}/requirements.txt +++ b/piptools-python/{{ cookiecutter.package_name }}/requirements.txt @@ -11,10 +11,10 @@ async-timeout==4.0.3 \ --hash=sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028 \ --hash=sha256:fd893e4cb1d9dff66d0e0576cc6376a23eacbbcd43b6607e4d3ecfae1ab15367 # via cli-base-utilities -bx-py-utils==102 \ - --hash=sha256:367d2bde9c7f2bf750f55748d4832daaf9df73e753b5efb351ec1a3899cc5d80 \ - --hash=sha256:6d131d40394b477de715169e80067a0ab4891c8f04afd33fbd7ca00e2faf21ae \ - --hash=sha256:961a0abf31b512f72c1473a4d115096b0c5becd32d08338ac62adbf5b217b680 +bx-py-utils==104 \ + --hash=sha256:508cfc1d0fa6c22298f697c4efaa913337847d488d8a53eeccfae9ee106123f6 \ + --hash=sha256:77fddff99aaf2cb558ce2974ef66d0e7a4d48cca1cbb99e43eca0d8b77fa07fc \ + --hash=sha256:c92ebc4fb122e3e3c228d984d0a1f5c3284c3da6aab1a1c753f7eb1f71bdab3a # via # cli-base-utilities # your_cool_package (pyproject.toml) diff --git a/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/cli_dev/code_style.py b/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/cli_dev/code_style.py index b623fa3..19b1667 100644 --- a/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/cli_dev/code_style.py +++ b/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/cli_dev/code_style.py @@ -2,6 +2,7 @@ from cli_base.cli_tools import code_style from cli_base.cli_tools.verbosity import OPTION_KWARGS_VERBOSE from cli_base.click_defaults import OPTION_ARGS_DEFAULT_TRUE + from {{ cookiecutter.package_name }}.cli_dev import PACKAGE_ROOT, cli @@ -10,7 +11,7 @@ @click.option('-v', '--verbosity', **OPTION_KWARGS_VERBOSE) def fix_code_style(color: bool, verbosity: int): """ - Fix code style of all cli_base source code files via darker + Fix code style of all {{ cookiecutter.package_name }} source code files via darker """ code_style.fix(package_root=PACKAGE_ROOT, darker_color=color, darker_verbose=verbosity > 0) diff --git a/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/cli_dev/packaging.py b/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/cli_dev/packaging.py index 94d4230..7614687 100644 --- a/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/cli_dev/packaging.py +++ b/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/cli_dev/packaging.py @@ -1,20 +1,21 @@ import sys from pathlib import Path -import cli_base import click from cli_base.cli_tools.dev_tools import run_unittest_cli from cli_base.cli_tools.subprocess_utils import verbose_check_call from cli_base.cli_tools.verbosity import OPTION_KWARGS_VERBOSE from cli_base.run_pip_audit import run_pip_audit from manageprojects.utilities.publish import publish_package + +import {{ cookiecutter.package_name }} from {{ cookiecutter.package_name }}.cli_dev import PACKAGE_ROOT, cli @cli.command() def install(): """ - Run pip-sync and install 'cli_base' via pip as editable. + Run pip-sync and install '{{ cookiecutter.package_name }}' via pip as editable. """ verbose_check_call('pip-sync', PACKAGE_ROOT / 'requirements.dev.txt') verbose_check_call('pip', 'install', '--no-deps', '-e', '.') @@ -69,6 +70,9 @@ def update(): # Install new dependencies in current .venv: verbose_check_call(bin_path / 'pip-sync', 'requirements.dev.txt') + # Update git pre-commit hooks: + verbose_check_call(bin_path / 'pre_commit', 'autoupdate') + @cli.command() def publish(): @@ -77,8 +81,4 @@ def publish(): """ run_unittest_cli(verbose=False, exit_after_run=False) # Don't publish a broken state - publish_package( - module=cli_base, - package_path=PACKAGE_ROOT, - distribution_name='cli-base-utilities', - ) + publish_package(module={{ cookiecutter.package_name }}, package_path=PACKAGE_ROOT) diff --git a/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/tests/test_project_setup.py b/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/tests/test_project_setup.py index 0f5c7c2..a97836e 100644 --- a/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/tests/test_project_setup.py +++ b/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/tests/test_project_setup.py @@ -4,6 +4,7 @@ from bx_py_utils.path import assert_is_file from manageprojects.test_utils.project_setup import check_editor_config, get_py_max_line_length from cli_base.cli_tools.code_style import assert_code_style +from cli_base.cli_tools.subprocess_utils import ToolsExecutor from packaging.version import Version from {{ cookiecutter.package_name }} import __version__ @@ -38,3 +39,8 @@ def test_check_editor_config(self): max_line_length = get_py_max_line_length(package_root=PACKAGE_ROOT) self.assertEqual(max_line_length, 119) + + def test_pre_commit_hooks(self): + executor = ToolsExecutor(cwd=PACKAGE_ROOT) + for command in ('migrate-config', 'validate-config', 'validate-manifest'): + executor.verbose_check_call('pre-commit', command, exit_on_error=True) diff --git a/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/tests/test_readme.py b/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/tests/test_readme.py new file mode 100644 index 0000000..c07d60f --- /dev/null +++ b/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/tests/test_readme.py @@ -0,0 +1,49 @@ +from bx_py_utils.auto_doc import assert_readme_block +from bx_py_utils.path import assert_is_file +from manageprojects.test_utils.click_cli_utils import invoke_click +from manageprojects.tests.base import BaseTestCase + +from {{ cookiecutter.package_name }} import constants +from {{ cookiecutter.package_name }}.cli_app import cli +from {{ cookiecutter.package_name }}.cli_dev import PACKAGE_ROOT +from {{ cookiecutter.package_name }}.cli_dev import cli as dev_cli + + +def assert_cli_help_in_readme(text_block: str, marker: str): + README_PATH = PACKAGE_ROOT / 'README.md' + assert_is_file(README_PATH) + + text_block = text_block.replace(constants.CLI_EPILOG, '') + text_block = f'```\n{text_block.strip()}\n```' + assert_readme_block( + readme_path=README_PATH, + text_block=text_block, + start_marker_line=f'[comment]: <> (✂✂✂ auto generated {marker} start ✂✂✂)', + end_marker_line=f'[comment]: <> (✂✂✂ auto generated {marker} end ✂✂✂)', + ) + + +class ReadmeTestCase(BaseTestCase): + def test_main_help(self): + stdout = invoke_click(cli, '--help') + self.assert_in_content( + got=stdout, + parts=( + 'Usage: ./cli.py [OPTIONS] COMMAND [ARGS]...', + constants.CLI_EPILOG, + ), + ) + assert_cli_help_in_readme(text_block=stdout, marker='main help') + + def test_dev_help(self): + stdout = invoke_click(dev_cli, '--help') + self.assert_in_content( + got=stdout, + parts=( + 'Usage: ./dev-cli.py [OPTIONS] COMMAND [ARGS]...', + ' check-code-style ', + ' coverage ', + constants.CLI_EPILOG, + ), + ) + assert_cli_help_in_readme(text_block=stdout, marker='dev help') diff --git a/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/tests/test_readme_history.py b/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/tests/test_readme_history.py new file mode 100644 index 0000000..4a97668 --- /dev/null +++ b/piptools-python/{{ cookiecutter.package_name }}/{{ cookiecutter.package_name }}/tests/test_readme_history.py @@ -0,0 +1,29 @@ +import os +from unittest import TestCase, skipIf + +from bx_py_utils.auto_doc import assert_readme_block +from cli_base.cli_tools.git_history import get_git_history + +import {{ cookiecutter.package_name }} +from {{ cookiecutter.package_name }}.cli_dev import PACKAGE_ROOT + + +class ReadmeHistoryTestCase(TestCase): + @skipIf( + # After a release the history may be "changed" because of version bump + # and we should not block merge requests because of this. + 'GITHUB_ACTION' in os.environ, + reason='Skip on github actions', + ) + def test_readme_history(self): + git_history = get_git_history( + current_version={{ cookiecutter.package_name }}.__version__, + add_author=False, + ) + history = '\n'.join(git_history) + assert_readme_block( + readme_path=PACKAGE_ROOT / 'README.md', + text_block=f'\n{history}\n', + start_marker_line='[comment]: <> (✂✂✂ auto generated history start ✂✂✂)', + end_marker_line='[comment]: <> (✂✂✂ auto generated history end ✂✂✂)', + )