tempest-cli 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. tempest_cli-0.1.0/.github/workflows/ci.yml +40 -0
  2. tempest_cli-0.1.0/.github/workflows/docs.yml +63 -0
  3. tempest_cli-0.1.0/.github/workflows/release-pypi.yml +135 -0
  4. tempest_cli-0.1.0/.gitignore +12 -0
  5. tempest_cli-0.1.0/CHANGELOG.md +53 -0
  6. tempest_cli-0.1.0/LICENSE +21 -0
  7. tempest_cli-0.1.0/Makefile +57 -0
  8. tempest_cli-0.1.0/PKG-INFO +167 -0
  9. tempest_cli-0.1.0/README.md +136 -0
  10. tempest_cli-0.1.0/docs/changelog.en.md +31 -0
  11. tempest_cli-0.1.0/docs/changelog.md +31 -0
  12. tempest_cli-0.1.0/docs/commands.en.md +74 -0
  13. tempest_cli-0.1.0/docs/commands.md +74 -0
  14. tempest_cli-0.1.0/docs/configuration.en.md +68 -0
  15. tempest_cli-0.1.0/docs/configuration.md +66 -0
  16. tempest_cli-0.1.0/docs/index.en.md +71 -0
  17. tempest_cli-0.1.0/docs/index.md +70 -0
  18. tempest_cli-0.1.0/docs/installation.en.md +61 -0
  19. tempest_cli-0.1.0/docs/installation.md +59 -0
  20. tempest_cli-0.1.0/docs/library.en.md +96 -0
  21. tempest_cli-0.1.0/docs/library.md +96 -0
  22. tempest_cli-0.1.0/docs/pr-prompt.en.md +78 -0
  23. tempest_cli-0.1.0/docs/pr-prompt.md +79 -0
  24. tempest_cli-0.1.0/docs/reference.en.md +17 -0
  25. tempest_cli-0.1.0/docs/reference.md +48 -0
  26. tempest_cli-0.1.0/mkdocs.yml +140 -0
  27. tempest_cli-0.1.0/pyproject.toml +151 -0
  28. tempest_cli-0.1.0/tempest_cli/__init__.py +70 -0
  29. tempest_cli-0.1.0/tempest_cli/_templates/pull_request_template.en-US.md +39 -0
  30. tempest_cli-0.1.0/tempest_cli/_templates/pull_request_template.pt-BR.md +39 -0
  31. tempest_cli-0.1.0/tempest_cli/config.py +164 -0
  32. tempest_cli-0.1.0/tempest_cli/lint.py +238 -0
  33. tempest_cli-0.1.0/tempest_cli/main.py +411 -0
  34. tempest_cli-0.1.0/tempest_cli/pr_prompt.py +779 -0
  35. tempest_cli-0.1.0/tempest_cli/py.typed +0 -0
  36. tempest_cli-0.1.0/tests/__init__.py +1 -0
  37. tempest_cli-0.1.0/tests/test_cli.py +102 -0
  38. tempest_cli-0.1.0/tests/test_config.py +73 -0
  39. tempest_cli-0.1.0/tests/test_lint_runners.py +154 -0
  40. tempest_cli-0.1.0/tests/test_lint_strictness.py +39 -0
  41. tempest_cli-0.1.0/tests/test_pr_prompt.py +336 -0
  42. tempest_cli-0.1.0/uv.lock +1289 -0
@@ -0,0 +1,40 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches:
8
+ - main
9
+ - "feat/**"
10
+ - "fix/**"
11
+ - "ref/**"
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ python:
18
+ name: Python ${{ matrix.python-version }}
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ python-version: ["3.11", "3.12", "3.13"]
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+
27
+ - name: Install uv
28
+ uses: astral-sh/setup-uv@v3
29
+ with:
30
+ enable-cache: true
31
+
32
+ - name: Set up Python ${{ matrix.python-version }}
33
+ run: uv python install ${{ matrix.python-version }}
34
+
35
+ - name: Install project
36
+ run: uv sync --all-extras
37
+
38
+ # The gate runs itself: a break in the runners fails here first.
39
+ - name: Quality gate (dogfooded)
40
+ run: uv run tempest-cli check
@@ -0,0 +1,63 @@
1
+ name: Deploy docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "docs/**"
8
+ - "mkdocs.yml"
9
+ - "README.md"
10
+ - "CHANGELOG.md"
11
+ - "tempest_cli/**"
12
+ - ".github/workflows/docs.yml"
13
+ - "pyproject.toml"
14
+ workflow_dispatch:
15
+
16
+ permissions:
17
+ contents: read
18
+ pages: write
19
+ id-token: write
20
+
21
+ concurrency:
22
+ group: pages
23
+ cancel-in-progress: false
24
+
25
+ jobs:
26
+ build:
27
+ name: Build site
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ with:
32
+ fetch-depth: 0
33
+
34
+ - name: Install uv
35
+ uses: astral-sh/setup-uv@v3
36
+ with:
37
+ enable-cache: true
38
+
39
+ - name: Set up Python
40
+ run: uv python install 3.13
41
+
42
+ - name: Install docs dependencies
43
+ run: uv sync --group docs
44
+
45
+ - name: Build site
46
+ run: uv run mkdocs build --strict --site-dir site
47
+
48
+ - name: Upload Pages artifact
49
+ uses: actions/upload-pages-artifact@v3
50
+ with:
51
+ path: site
52
+
53
+ deploy:
54
+ name: Deploy to GitHub Pages
55
+ needs: build
56
+ runs-on: ubuntu-latest
57
+ environment:
58
+ name: github-pages
59
+ url: ${{ steps.deployment.outputs.page_url }}
60
+ steps:
61
+ - name: Deploy to GitHub Pages
62
+ id: deployment
63
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,135 @@
1
+ name: Release to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ build:
14
+ name: Build distributions
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v3
21
+ with:
22
+ enable-cache: true
23
+
24
+ - name: Set up Python
25
+ run: uv python install 3.13
26
+
27
+ - name: Verify tag matches project version
28
+ run: |
29
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
30
+ PYPROJECT_VERSION=$(python -c "import tomllib; print(tomllib.loads(open('pyproject.toml','rb').read().decode())['project']['version'])")
31
+ PACKAGE_VERSION=$(python -c "import re; print(re.search(r'__version__:\s*str\s*=\s*\"([^\"]+)\"', open('tempest_cli/__init__.py').read()).group(1))")
32
+ echo "Tag: $TAG_VERSION"
33
+ echo "pyproject: $PYPROJECT_VERSION"
34
+ echo "__version__: $PACKAGE_VERSION"
35
+ if [ "$TAG_VERSION" != "$PYPROJECT_VERSION" ] || [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then
36
+ echo "Version mismatch — bump pyproject.toml AND tempest_cli/__init__.py to match the tag before retagging."
37
+ exit 1
38
+ fi
39
+
40
+ - name: Install project
41
+ run: uv sync --all-extras
42
+
43
+ - name: Quality gate (dogfooded)
44
+ run: uv run tempest-cli check
45
+
46
+ - name: Build sdist and wheel
47
+ run: uv build
48
+
49
+ - name: Verify metadata
50
+ run: |
51
+ uv tool install twine
52
+ uv tool run twine check dist/*
53
+
54
+ - name: Verify wheel carries the package and the PR templates
55
+ run: |
56
+ python -c "import zipfile, glob; \
57
+ wheel = glob.glob('dist/*.whl')[0]; \
58
+ members = zipfile.ZipFile(wheel).namelist(); \
59
+ py_files = [n for n in members if n.startswith('tempest_cli/') and n.endswith('.py')]; \
60
+ templates = [n for n in members if n.endswith('.md') and '_templates' in n]; \
61
+ print('Python files:', len(py_files), '| PR templates:', len(templates)); \
62
+ assert len(py_files) >= 5, f'wheel suspiciously empty ({len(py_files)} files)'; \
63
+ assert len(templates) == 2, 'bundled PR templates missing from wheel'; \
64
+ print('OK')"
65
+
66
+ - name: Smoke-install built wheel in a clean venv
67
+ run: |
68
+ uv venv --python 3.11 /tmp/smoke-venv
69
+ uv pip install --python /tmp/smoke-venv/bin/python --quiet "$(ls dist/*.whl)"
70
+ /tmp/smoke-venv/bin/python -c "import sys, tempest_cli as m; \
71
+ assert m.__version__, 'no __version__'; \
72
+ assert m.run_full_check and m.load_tempest_config and m.generate_pr_prompt, 'surface missing'; \
73
+ heavy = {'fastapi', 'sqlalchemy', 'alembic', 'starlette', 'pydantic'}; \
74
+ import tempest_cli.main; \
75
+ loaded = {name.split('.')[0] for name in sys.modules}; \
76
+ assert not (heavy & loaded), f'pulled a web framework: {sorted(heavy & loaded)}'; \
77
+ print('Smoke OK · version =', m.__version__)"
78
+ /tmp/smoke-venv/bin/tempest-cli --version
79
+
80
+ - name: Upload distributions artifact
81
+ uses: actions/upload-artifact@v4
82
+ with:
83
+ name: python-dist
84
+ path: dist/
85
+
86
+ publish:
87
+ name: Publish to PyPI
88
+ needs: build
89
+ runs-on: ubuntu-latest
90
+ environment:
91
+ name: pypi
92
+ url: https://pypi.org/p/tempest-cli
93
+ permissions:
94
+ id-token: write
95
+ steps:
96
+ - name: Download distributions artifact
97
+ uses: actions/download-artifact@v4
98
+ with:
99
+ name: python-dist
100
+ path: dist
101
+
102
+ - name: Publish to PyPI (Trusted Publishing)
103
+ uses: pypa/gh-action-pypi-publish@release/v1
104
+
105
+ github_release:
106
+ name: GitHub Release
107
+ needs: publish
108
+ runs-on: ubuntu-latest
109
+ permissions:
110
+ contents: write
111
+ steps:
112
+ - uses: actions/checkout@v4
113
+
114
+ - name: Create GitHub Release from the CHANGELOG section
115
+ env:
116
+ GH_TOKEN: ${{ github.token }}
117
+ run: |
118
+ VERSION="${GITHUB_REF_NAME#v}"
119
+ python - "$VERSION" > notes.md <<'PY'
120
+ import re, sys
121
+ version = sys.argv[1]
122
+ changelog = open("CHANGELOG.md").read()
123
+ match = re.search(
124
+ r"## \[" + re.escape(version) + r"\][^\n]*\n(.*?)(?=\n## \[|\Z)",
125
+ changelog,
126
+ re.DOTALL,
127
+ )
128
+ print(match.group(1).strip() if match else "See CHANGELOG.md.")
129
+ PY
130
+ if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then
131
+ gh release edit "$GITHUB_REF_NAME" --notes-file notes.md --latest
132
+ else
133
+ gh release create "$GITHUB_REF_NAME" \
134
+ --title "$GITHUB_REF_NAME" --notes-file notes.md --verify-tag --latest
135
+ fi
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+ .coverage
11
+ htmlcov/
12
+ site/
@@ -0,0 +1,53 @@
1
+ # Changelog
2
+
3
+ All notable changes to **tempest-cli** are listed below.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] — 2026-08-15
9
+
10
+ First release. Extracted from
11
+ [`tempest-fastapi-sdk`](https://github.com/mauriciobenjamin700/tempest-fastapi-sdk),
12
+ where the same gate shipped as `tempest check` — reachable only by
13
+ installing a FastAPI SDK.
14
+
15
+ ### Added
16
+
17
+ - **The quality gate**: `lint`, `fix`, `format`, `fmt-check`, `type`,
18
+ `test` and `check`, over `ruff` / `mypy` / `pytest`. Each returns the
19
+ underlying tool's exit code; `check` runs the four in order and stops
20
+ at the first failure.
21
+
22
+ - **Typing strictness as configuration**, read from `[tool.tempest]
23
+ typing_strictness` in `pyproject.toml` (`lenient` | `standard` |
24
+ `strict`, defaulting to `standard`), with a `--strictness` / `-s`
25
+ override per run. Levels only ever **add** flags on top of the
26
+ project's own `[tool.ruff]` / `[tool.mypy]`. `ANN401` is never enabled
27
+ at any level: `Any` is a legitimate annotation.
28
+
29
+ - **`pr-prompt`** — builds the prompt that makes an AI write a branch's
30
+ pull-request description, carrying the repository's own PR template
31
+ (or a bundled PT-BR / EN-US default), the rules that stop a model from
32
+ returning the template unfilled, and the branch context read as
33
+ `base...head`.
34
+
35
+ - **A library surface**: `run_full_check`, `run_ruff_check`,
36
+ `run_ruff_fix`, `run_ruff_format`, `run_mypy`, `run_pytest`,
37
+ `load_tempest_config`, `find_pyproject`, `generate_pr_prompt`.
38
+
39
+ - **`register_commands(app)`** mounts the whole gate onto an existing
40
+ `typer.Typer`, so another CLI exposes these commands under its own
41
+ name without copying a body. `tempest-fastapi-sdk` uses exactly this,
42
+ which is what keeps `tempest check` and `tempest-cli check` identical.
43
+
44
+ ### Notes
45
+
46
+ - The only runtime dependency is `typer`. `ruff`, `mypy` and `pytest`
47
+ are invoked from the active environment (or through `uv run` when they
48
+ are not on `PATH`), so the project pins the versions it wants. The
49
+ `[tools]` extra installs all three for whoever prefers that.
50
+ - Measured against the SDK it came from: reaching these commands there
51
+ loaded 38.7 MB of dependencies and about 0.5 s of import time per
52
+ invocation — for four commands that touch none of it. A test in this
53
+ package asserts that importing it pulls no web framework.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mauricio Benjamin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,57 @@
1
+ .DEFAULT_GOAL := help
2
+ PACKAGE := tempest_cli
3
+
4
+ .PHONY: help install check lint fix fmt fmt-check type test docs docs-serve build smoke clean
5
+
6
+ help: ## Show this help
7
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-12s\033[0m %s\n", $$1, $$2}'
8
+
9
+ install: ## Sync every dependency group
10
+ uv sync --all-extras --group docs
11
+
12
+ check: ## Run the gate on itself (lint + fmt-check + type + test)
13
+ uv run tempest-cli check
14
+
15
+ lint: ## ruff check
16
+ uv run tempest-cli lint
17
+
18
+ fix: ## Every ruff autofix, then format
19
+ uv run tempest-cli fix
20
+
21
+ fmt: ## ruff format (writes)
22
+ uv run tempest-cli format
23
+
24
+ fmt-check: ## ruff format --check
25
+ uv run tempest-cli fmt-check
26
+
27
+ type: ## mypy
28
+ uv run tempest-cli type
29
+
30
+ test: ## pytest
31
+ uv run tempest-cli test
32
+
33
+ docs: ## Build the bilingual site (strict)
34
+ uv run --group docs mkdocs build --strict
35
+
36
+ docs-serve: ## Serve the docs locally
37
+ uv run --group docs mkdocs serve
38
+
39
+ build: ## Build sdist + wheel
40
+ uv build
41
+
42
+ smoke: ## Install the built wheel in a clean venv and import it
43
+ @rm -rf /tmp/$(PACKAGE)-smoke dist
44
+ uv build
45
+ uv venv --python 3.11 /tmp/$(PACKAGE)-smoke
46
+ uv pip install --python /tmp/$(PACKAGE)-smoke/bin/python --quiet "$$(ls dist/*.whl)"
47
+ /tmp/$(PACKAGE)-smoke/bin/python -c "import sys, tempest_cli as m, tempest_cli.main; \
48
+ assert m.__version__, 'no __version__'; \
49
+ heavy = {'fastapi', 'sqlalchemy', 'alembic', 'starlette', 'pydantic'}; \
50
+ loaded = {name.split('.')[0] for name in sys.modules}; \
51
+ assert not (heavy & loaded), f'pulled a web framework: {sorted(heavy & loaded)}'; \
52
+ print('Smoke OK · version =', m.__version__)"
53
+ /tmp/$(PACKAGE)-smoke/bin/tempest-cli --version
54
+ @rm -rf /tmp/$(PACKAGE)-smoke
55
+
56
+ clean: ## Remove build and cache artifacts
57
+ rm -rf dist build site .pytest_cache .mypy_cache .ruff_cache htmlcov .coverage
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.5
2
+ Name: tempest-cli
3
+ Version: 0.1.0
4
+ Summary: Framework-agnostic quality gate for Python projects: ruff + mypy + pytest behind one command, with a typing-strictness dial and a PR-description prompt generator.
5
+ Project-URL: Homepage, https://github.com/mauriciobenjamin700/tempest-cli
6
+ Project-URL: Repository, https://github.com/mauriciobenjamin700/tempest-cli
7
+ Project-URL: Issues, https://github.com/mauriciobenjamin700/tempest-cli/issues
8
+ Project-URL: Documentation, https://mauriciobenjamin700.github.io/tempest-cli/
9
+ Author-email: Mauricio Benjamin <mauricio.benjamin@reloverelations.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: cli,lint,mypy,pytest,quality,ruff,typing
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Quality Assurance
22
+ Classifier: Topic :: Utilities
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.11
25
+ Requires-Dist: typer>=0.12.0
26
+ Provides-Extra: tools
27
+ Requires-Dist: mypy>=1.13.0; extra == 'tools'
28
+ Requires-Dist: pytest>=8.3.3; extra == 'tools'
29
+ Requires-Dist: ruff>=0.8.0; extra == 'tools'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # tempest-cli
33
+
34
+ [![PyPI](https://img.shields.io/pypi/v/tempest-cli.svg)](https://pypi.org/project/tempest-cli/)
35
+ [![Python](https://img.shields.io/pypi/pyversions/tempest-cli.svg)](https://pypi.org/project/tempest-cli/)
36
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
37
+
38
+ **Documentação: [Português (BR)](https://mauriciobenjamin700.github.io/tempest-cli/) · [English (US)](https://mauriciobenjamin700.github.io/tempest-cli/en/)**
39
+
40
+ One command for the quality gate of any Python project — `ruff` +
41
+ `mypy` + `pytest`, with a typing-strictness dial that lives in your
42
+ `pyproject.toml` instead of in four different Makefile targets.
43
+
44
+ Framework-agnostic on purpose: Django, Flask, Litestar, FastAPI, a
45
+ library, a script. The only runtime dependency is `typer`.
46
+
47
+ ```bash
48
+ uv add --dev tempest-cli
49
+
50
+ tempest-cli check # lint + fmt-check + type + test, in order, stops at the first failure
51
+ tempest-cli fix # every ruff autofix, then format
52
+ tempest-cli type -s strict # override the configured strictness for one run
53
+ ```
54
+
55
+ ## Why it exists
56
+
57
+ The four commands are always the same, and every project rewrites them
58
+ slightly differently — a Makefile here, a `tox.ini` there, a CI job that
59
+ drifts from what runs locally. `tempest-cli check` is the same gate on
60
+ your machine and in CI, and `--strictness` makes "how much typing do we
61
+ enforce" a value in `pyproject.toml` rather than a flag someone
62
+ remembered to pass.
63
+
64
+ ## Commands
65
+
66
+ | Command | Runs |
67
+ | --- | --- |
68
+ | `tempest-cli lint` | `ruff check` |
69
+ | `tempest-cli fix` | `ruff check --fix` then `ruff format` (`--unsafe` for the risky autofixes) |
70
+ | `tempest-cli format` | `ruff format` (writes) |
71
+ | `tempest-cli fmt-check` | `ruff format --check` (read-only) |
72
+ | `tempest-cli type` | `mypy` |
73
+ | `tempest-cli test` | `pytest` |
74
+ | `tempest-cli check` | all four, in order, stopping at the first failure |
75
+ | `tempest-cli pr-prompt` | builds the prompt that makes an AI write this branch's PR description |
76
+
77
+ Every command takes an optional path (`tempest-cli lint src/`) and
78
+ returns the underlying tool's exit code, so CI reads it exactly as it
79
+ would read `ruff` directly.
80
+
81
+ ## Typing strictness
82
+
83
+ ```toml
84
+ [tool.tempest]
85
+ typing_strictness = "strict" # lenient | standard | strict
86
+ ```
87
+
88
+ The level **adds** flags on top of your own `[tool.ruff]` /
89
+ `[tool.mypy]` — it never relaxes what you already configured:
90
+
91
+ | Level | ruff (extra `ANN` rules) | mypy |
92
+ | --- | --- | --- |
93
+ | `lenient` | none | none |
94
+ | `standard` | `ANN001`, `ANN201`, `ANN202`, `ANN205`, `ANN206` | `--disallow-untyped-defs --disallow-incomplete-defs` |
95
+ | `strict` | the above plus `ANN204` | `--strict` |
96
+
97
+ `ANN401` is never enabled at any level: `Any` is a legitimate
98
+ annotation. The levels enforce that things *are* annotated, never that
99
+ they avoid `Any`.
100
+
101
+ Override per run with `--strictness` / `-s`. Absent config means
102
+ `standard`.
103
+
104
+ ## PR descriptions from the branch itself
105
+
106
+ ```bash
107
+ tempest-cli pr-prompt | claude -p
108
+ tempest-cli pr-prompt develop --lang en --out pr_prompt.txt
109
+ ```
110
+
111
+ The prompt carries the repository's own pull-request template (or a
112
+ bundled PT-BR / EN-US default), the rules that stop a model from handing
113
+ back the template with its placeholders intact, and the branch context:
114
+ commit subjects, changed files, and a bounded excerpt of each patch.
115
+
116
+ Diffs are read as `base...head` — the merge-base diff the forge shows —
117
+ so commits that landed on the base after the branch started are not
118
+ attributed to it. Whatever the bounds leave out is stated inside the
119
+ prompt, so a partial diff reads as partial.
120
+
121
+ ## Use it as a library
122
+
123
+ ```python
124
+ from tempest_cli import load_tempest_config, run_full_check
125
+
126
+ config = load_tempest_config()
127
+ exit_code = run_full_check(".", config=config)
128
+ ```
129
+
130
+ And to expose the same gate from your own CLI, without copying command
131
+ bodies:
132
+
133
+ ```python
134
+ import typer
135
+
136
+ from tempest_cli.main import register_commands
137
+
138
+ cli: typer.Typer = typer.Typer(name="mytool")
139
+ register_commands(cli)
140
+ ```
141
+
142
+ ## Installing the tools
143
+
144
+ `tempest-cli` shells out to whatever `ruff`, `mypy` and `pytest` it
145
+ finds — it does not pin them, so your project chooses the versions. To
146
+ install them alongside it:
147
+
148
+ ```bash
149
+ uv add --dev "tempest-cli[tools]"
150
+ ```
151
+
152
+ ## Relationship with tempest-fastapi-sdk
153
+
154
+ This package was extracted from
155
+ [`tempest-fastapi-sdk`](https://github.com/mauriciobenjamin700/tempest-fastapi-sdk),
156
+ where the same gate shipped as `tempest check`. Getting it meant
157
+ installing FastAPI, SQLAlchemy, Alembic and Pydantic — 38.7 MB of
158
+ dependencies and roughly 0.5 s of import time per invocation, for four
159
+ commands that never touch any of it.
160
+
161
+ `tempest check` keeps working: the SDK depends on this package and
162
+ registers the same commands. Both stay in sync because there is now one
163
+ implementation.
164
+
165
+ ## License
166
+
167
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,136 @@
1
+ # tempest-cli
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/tempest-cli.svg)](https://pypi.org/project/tempest-cli/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/tempest-cli.svg)](https://pypi.org/project/tempest-cli/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ **Documentação: [Português (BR)](https://mauriciobenjamin700.github.io/tempest-cli/) · [English (US)](https://mauriciobenjamin700.github.io/tempest-cli/en/)**
8
+
9
+ One command for the quality gate of any Python project — `ruff` +
10
+ `mypy` + `pytest`, with a typing-strictness dial that lives in your
11
+ `pyproject.toml` instead of in four different Makefile targets.
12
+
13
+ Framework-agnostic on purpose: Django, Flask, Litestar, FastAPI, a
14
+ library, a script. The only runtime dependency is `typer`.
15
+
16
+ ```bash
17
+ uv add --dev tempest-cli
18
+
19
+ tempest-cli check # lint + fmt-check + type + test, in order, stops at the first failure
20
+ tempest-cli fix # every ruff autofix, then format
21
+ tempest-cli type -s strict # override the configured strictness for one run
22
+ ```
23
+
24
+ ## Why it exists
25
+
26
+ The four commands are always the same, and every project rewrites them
27
+ slightly differently — a Makefile here, a `tox.ini` there, a CI job that
28
+ drifts from what runs locally. `tempest-cli check` is the same gate on
29
+ your machine and in CI, and `--strictness` makes "how much typing do we
30
+ enforce" a value in `pyproject.toml` rather than a flag someone
31
+ remembered to pass.
32
+
33
+ ## Commands
34
+
35
+ | Command | Runs |
36
+ | --- | --- |
37
+ | `tempest-cli lint` | `ruff check` |
38
+ | `tempest-cli fix` | `ruff check --fix` then `ruff format` (`--unsafe` for the risky autofixes) |
39
+ | `tempest-cli format` | `ruff format` (writes) |
40
+ | `tempest-cli fmt-check` | `ruff format --check` (read-only) |
41
+ | `tempest-cli type` | `mypy` |
42
+ | `tempest-cli test` | `pytest` |
43
+ | `tempest-cli check` | all four, in order, stopping at the first failure |
44
+ | `tempest-cli pr-prompt` | builds the prompt that makes an AI write this branch's PR description |
45
+
46
+ Every command takes an optional path (`tempest-cli lint src/`) and
47
+ returns the underlying tool's exit code, so CI reads it exactly as it
48
+ would read `ruff` directly.
49
+
50
+ ## Typing strictness
51
+
52
+ ```toml
53
+ [tool.tempest]
54
+ typing_strictness = "strict" # lenient | standard | strict
55
+ ```
56
+
57
+ The level **adds** flags on top of your own `[tool.ruff]` /
58
+ `[tool.mypy]` — it never relaxes what you already configured:
59
+
60
+ | Level | ruff (extra `ANN` rules) | mypy |
61
+ | --- | --- | --- |
62
+ | `lenient` | none | none |
63
+ | `standard` | `ANN001`, `ANN201`, `ANN202`, `ANN205`, `ANN206` | `--disallow-untyped-defs --disallow-incomplete-defs` |
64
+ | `strict` | the above plus `ANN204` | `--strict` |
65
+
66
+ `ANN401` is never enabled at any level: `Any` is a legitimate
67
+ annotation. The levels enforce that things *are* annotated, never that
68
+ they avoid `Any`.
69
+
70
+ Override per run with `--strictness` / `-s`. Absent config means
71
+ `standard`.
72
+
73
+ ## PR descriptions from the branch itself
74
+
75
+ ```bash
76
+ tempest-cli pr-prompt | claude -p
77
+ tempest-cli pr-prompt develop --lang en --out pr_prompt.txt
78
+ ```
79
+
80
+ The prompt carries the repository's own pull-request template (or a
81
+ bundled PT-BR / EN-US default), the rules that stop a model from handing
82
+ back the template with its placeholders intact, and the branch context:
83
+ commit subjects, changed files, and a bounded excerpt of each patch.
84
+
85
+ Diffs are read as `base...head` — the merge-base diff the forge shows —
86
+ so commits that landed on the base after the branch started are not
87
+ attributed to it. Whatever the bounds leave out is stated inside the
88
+ prompt, so a partial diff reads as partial.
89
+
90
+ ## Use it as a library
91
+
92
+ ```python
93
+ from tempest_cli import load_tempest_config, run_full_check
94
+
95
+ config = load_tempest_config()
96
+ exit_code = run_full_check(".", config=config)
97
+ ```
98
+
99
+ And to expose the same gate from your own CLI, without copying command
100
+ bodies:
101
+
102
+ ```python
103
+ import typer
104
+
105
+ from tempest_cli.main import register_commands
106
+
107
+ cli: typer.Typer = typer.Typer(name="mytool")
108
+ register_commands(cli)
109
+ ```
110
+
111
+ ## Installing the tools
112
+
113
+ `tempest-cli` shells out to whatever `ruff`, `mypy` and `pytest` it
114
+ finds — it does not pin them, so your project chooses the versions. To
115
+ install them alongside it:
116
+
117
+ ```bash
118
+ uv add --dev "tempest-cli[tools]"
119
+ ```
120
+
121
+ ## Relationship with tempest-fastapi-sdk
122
+
123
+ This package was extracted from
124
+ [`tempest-fastapi-sdk`](https://github.com/mauriciobenjamin700/tempest-fastapi-sdk),
125
+ where the same gate shipped as `tempest check`. Getting it meant
126
+ installing FastAPI, SQLAlchemy, Alembic and Pydantic — 38.7 MB of
127
+ dependencies and roughly 0.5 s of import time per invocation, for four
128
+ commands that never touch any of it.
129
+
130
+ `tempest check` keeps working: the SDK depends on this package and
131
+ registers the same commands. Both stay in sync because there is now one
132
+ implementation.
133
+
134
+ ## License
135
+
136
+ MIT — see [LICENSE](LICENSE).