pyossmtool 0.0.1__tar.gz → 0.0.3__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.
- pyossmtool-0.0.3/PKG-INFO +259 -0
- pyossmtool-0.0.3/README.md +230 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/pyproject.toml +1 -1
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/__init__.py +1 -1
- pyossmtool-0.0.3/src/pyossmtool/bundle/suites/all.yaml +51 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/cli.py +49 -17
- pyossmtool-0.0.3/src/pyossmtool/error_formatters.py +218 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/models.py +26 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/reporter.py +1 -1
- pyossmtool-0.0.3/src/pyossmtool.egg-info/PKG-INFO +259 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool.egg-info/SOURCES.txt +3 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_check_modes.py +7 -0
- pyossmtool-0.0.3/tests/test_cli.py +49 -0
- pyossmtool-0.0.3/tests/test_error_formatters.py +117 -0
- pyossmtool-0.0.1/PKG-INFO +0 -94
- pyossmtool-0.0.1/README.md +0 -65
- pyossmtool-0.0.1/src/pyossmtool.egg-info/PKG-INFO +0 -94
- pyossmtool-0.0.1/tests/test_cli.py +0 -17
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/LICENSE +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/setup.cfg +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/bandit.scan.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/codespell.spelling.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/deadcode.scan.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/gate.acronym-allowlist.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/gate.folder-breadth.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/gate.module-private-vars.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/gate.module-size.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/gitleaks.secrets.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/hadolint.dockerfile.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/jscpd.duplication.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/markdownlint.check.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/mdformat.apply.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/mdformat.format.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/mutmut.run.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/pydeps.cycles.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/pytest.coverage.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/pytest.test.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/radon.cc.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/radon.mi.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/ruff.format.apply.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/ruff.format.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/ruff.lint.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/ruff.unused.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/semgrep.scan.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/shellcheck.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/shfmt.apply.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/shfmt.format.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/sourcery.review.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/ty.check.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/vulture.scan.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/yamlfmt.apply.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/yamlfmt.format.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/checks/yamllint.check.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/bandit.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/codespell.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/deadcode.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/gitleaks.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/hadolint.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/jscpd.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/markdownlint.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/mdformat.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/mutmut.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/pydeps.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/pytest.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/radon.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/ruff.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/script.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/semgrep.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/shellcheck.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/shfmt.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/sourcery.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/ty.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/vulture.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/yamlfmt.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/catalog/tools/yamllint.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/allowlists/acronyms.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/allowlists/folder-breadth.txt +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/allowlists/module-private-vars.txt +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/allowlists/module-size.txt +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/bandit.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/gates/acronym-allowlist.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/gates/folder-breadth.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/gates/module-private-vars.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/gates/module-size.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/markdownlint.json +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/ruff.toml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/ty.toml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/yamlfmt.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/configs/yamllint.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/gates/README.md +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/gates/acronym-allowlist.sh +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/gates/folder-breadth.sh +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/gates/lib.sh +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/gates/module-private-vars.sh +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/gates/module-size.sh +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/defaults/semgrep/python-quality.yml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/demo.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/extended.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/format.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/formatting.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/policy.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/python-quality.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/reslab-parity.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/bundle/suites/standard.yaml +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/config_resolver.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/discovery.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/gate_config.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/gates.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/ignore.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/ignore_materialize.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/installer.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/__init__.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/analysis.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/base.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/common.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/format_text.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/gates.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/patterns.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/prose.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/ruff.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/parsers/shell.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/policy/__init__.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/policy/acronym_allowlist.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/policy/folder_breadth.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/registry.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/resolver.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/runner.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/runner_script.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool/target_expand.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool.egg-info/dependency_links.txt +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool.egg-info/entry_points.txt +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool.egg-info/requires.txt +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/src/pyossmtool.egg-info/top_level.txt +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_config_and_targets.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_discovery.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_gate_config.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_ignore.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_npm_install.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_packaging.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_parser_registry.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_script_gates.py +0 -0
- {pyossmtool-0.0.1 → pyossmtool-0.0.3}/tests/test_wheel_install.py +0 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyossmtool
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: Quality-gate orchestrator for AI-assisted development
|
|
5
|
+
Author-email: Vishal Kumar Mishra <vishal.k.mishra2@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Changelog, https://github.com/inquilabee/pyossmtool/blob/main/CHANGELOG.md
|
|
8
|
+
Project-URL: Homepage, https://github.com/inquilabee/pyossmtool
|
|
9
|
+
Project-URL: Issues, https://github.com/inquilabee/pyossmtool/issues
|
|
10
|
+
Project-URL: Repository, https://github.com/inquilabee/pyossmtool
|
|
11
|
+
Keywords: quality,lint,format,pre-commit,python,static-analysis
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
21
|
+
Requires-Python: <3.14,>=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: pydantic>=2.0
|
|
25
|
+
Requires-Dist: pathspec>=0.12
|
|
26
|
+
Requires-Dist: pyyaml>=6.0
|
|
27
|
+
Requires-Dist: typer>=0.12
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# pyossmtool
|
|
31
|
+
|
|
32
|
+
Quality gates for Python repos that developers can run locally, in CI, or hand to an AI agent without inventing a new workflow each time.
|
|
33
|
+
|
|
34
|
+
`pyossmtool` gives you one project config, a bundled catalog of checks, quiet success, and structured failure reports. It is inspired by Trunk and pre-commit, but keeps the surface area small:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pyossmtool install
|
|
38
|
+
pyossmtool format
|
|
39
|
+
pyossmtool check
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install pyossmtool
|
|
46
|
+
# or
|
|
47
|
+
uv add --dev pyossmtool
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Requires Python 3.11-3.13.
|
|
51
|
+
|
|
52
|
+
## 60-Second Setup
|
|
53
|
+
|
|
54
|
+
Create `pyossmtool.yaml`:
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
suite: all
|
|
58
|
+
env: auto
|
|
59
|
+
target: .
|
|
60
|
+
error-format: compact
|
|
61
|
+
configs:
|
|
62
|
+
mode: auto
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Then run:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pyossmtool install # install tools needed by suite:
|
|
69
|
+
pyossmtool format # apply formatter/autofix checks
|
|
70
|
+
pyossmtool check # report-only quality checks
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The `suite:` value is the project default. You do not need to repeat `--suite all`; the CLI reads it from `pyossmtool.yaml`.
|
|
74
|
+
|
|
75
|
+
Success is silent and exits `0`. Failures exit `1`, write a JSON report under `reports/failures/`, and print the same report through your configured `error-format`.
|
|
76
|
+
|
|
77
|
+
## Mental Model
|
|
78
|
+
|
|
79
|
+
- **Suite**: a named checklist, such as `python-quality`, `standard`, or `all`.
|
|
80
|
+
- **Check**: one rule runner inside a suite, such as `ruff.lint` or `ty.check`.
|
|
81
|
+
- **`check`**: report-only. It should not rewrite your files.
|
|
82
|
+
- **`format`**: applies write/fix checks, such as formatters.
|
|
83
|
+
- **`install`**: installs the tools needed by the selected suite.
|
|
84
|
+
|
|
85
|
+
Most teams pick a suite once in `pyossmtool.yaml` and run the same three commands everywhere.
|
|
86
|
+
|
|
87
|
+
## Config That Matters
|
|
88
|
+
|
|
89
|
+
```yaml
|
|
90
|
+
suite: python-quality # default checklist for install/check/format
|
|
91
|
+
env: auto # auto | managed | project
|
|
92
|
+
target: . # scan root; defaults to .
|
|
93
|
+
error-format: compact # json | log | text | compact | github
|
|
94
|
+
|
|
95
|
+
configs:
|
|
96
|
+
mode: auto # repo configs first, bundled fallback
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Use `--suite` only when you want a one-off override:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pyossmtool check --suite extended
|
|
103
|
+
pyossmtool install --suite standard
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Copy [`pyossmtool.yaml.example`](pyossmtool.yaml.example) for an annotated config with ignores, custom checks, gates, and custom error formatters.
|
|
107
|
+
|
|
108
|
+
## Suites
|
|
109
|
+
|
|
110
|
+
Suites are bundled starting points. They choose which checks run; tools still discover their own files and `.gitignore` is always respected.
|
|
111
|
+
|
|
112
|
+
| Suite | Use it for |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| `all` | Run every bundled check |
|
|
115
|
+
| `python-quality` | Core Python lint, format-check, and type checks |
|
|
116
|
+
| `formatting` | Report-only formatting drift |
|
|
117
|
+
| `format` | Formatter/autofix checks that write files |
|
|
118
|
+
| `standard` | Broader portable quality baseline |
|
|
119
|
+
| `extended` | Slower optional checks on top of standard |
|
|
120
|
+
| `policy` | Bundled script gates |
|
|
121
|
+
| `demo` | Internal/sample fixture suite |
|
|
122
|
+
|
|
123
|
+
Examples:
|
|
124
|
+
|
|
125
|
+
```yaml
|
|
126
|
+
# Local Python repo
|
|
127
|
+
suite: python-quality
|
|
128
|
+
error-format: text
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```yaml
|
|
132
|
+
# CI baseline with GitHub annotations
|
|
133
|
+
suite: standard
|
|
134
|
+
error-format: github
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```yaml
|
|
138
|
+
# Max coverage while developing pyossmtool itself
|
|
139
|
+
suite: all
|
|
140
|
+
error-format: compact
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Error Output
|
|
144
|
+
|
|
145
|
+
On failure, pyossmtool always writes the canonical JSON report to disk:
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
reports/failures/ruff.lint-20260716T163000Z/report.json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`error-format` only controls what is printed to stderr.
|
|
152
|
+
|
|
153
|
+
| Format | Example output |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| `json` | Pretty JSON report, including `report_path` |
|
|
156
|
+
| `log` | `2026-07-16T12:00:00+00:00 [error] ruff.lint/E501 src/app.py:42: Line too long` |
|
|
157
|
+
| `text` | `- [error] E501: Line too long (src/app.py:42)` |
|
|
158
|
+
| `compact` | `src/app.py:42: error: E501 Line too long` |
|
|
159
|
+
| `github` | `::error file=src/app.py,title=ruff.lint/E501,line=42::Line too long` |
|
|
160
|
+
|
|
161
|
+
Default is `json` when `error-format` is omitted.
|
|
162
|
+
|
|
163
|
+
Custom formatters live in `pyossmtool.yaml`:
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
suite: all
|
|
167
|
+
error-format: short
|
|
168
|
+
error-formatters:
|
|
169
|
+
short:
|
|
170
|
+
kind: finding_line
|
|
171
|
+
template: "{severity}\t{rule_id}\t{file}:{line}\t{message}"
|
|
172
|
+
jq-summary:
|
|
173
|
+
kind: jq
|
|
174
|
+
program: >
|
|
175
|
+
.findings[] | "[\(.severity)] \(.rule_id) \(.message)"
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`finding_line` placeholders: `severity`, `rule_id`, `message`, `file`, `line`, `check_id`, `report_path`.
|
|
179
|
+
|
|
180
|
+
`jq` formatters require `jq` on `PATH`.
|
|
181
|
+
|
|
182
|
+
## Daily Commands
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
pyossmtool install
|
|
186
|
+
pyossmtool format
|
|
187
|
+
pyossmtool check
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Inspect what is available:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
pyossmtool list suites
|
|
194
|
+
pyossmtool list tools
|
|
195
|
+
pyossmtool list checks
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Run a single check:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
pyossmtool check --check ruff.lint --target .
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Export the failure report schema:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
pyossmtool schema > failure-report.schema.json
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Project-Local Gates
|
|
211
|
+
|
|
212
|
+
Use gates when a repo needs a policy that is not covered by the bundled catalog.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
pyossmtool gate init module-size --description "Cap module line counts"
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
That creates a shell gate under `.pyossmtool/gates/` and a catalog entry under `.pyossmtool/catalog/checks/`. Enable it from `pyossmtool.yaml`:
|
|
219
|
+
|
|
220
|
+
```yaml
|
|
221
|
+
checks:
|
|
222
|
+
- id: gate.module-size
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## CI
|
|
226
|
+
|
|
227
|
+
Minimal GitHub Actions job:
|
|
228
|
+
|
|
229
|
+
```yaml
|
|
230
|
+
name: quality
|
|
231
|
+
|
|
232
|
+
on:
|
|
233
|
+
pull_request:
|
|
234
|
+
push:
|
|
235
|
+
branches: [main]
|
|
236
|
+
|
|
237
|
+
jobs:
|
|
238
|
+
pyossmtool:
|
|
239
|
+
runs-on: ubuntu-latest
|
|
240
|
+
steps:
|
|
241
|
+
- uses: actions/checkout@v4
|
|
242
|
+
- uses: astral-sh/setup-uv@v6
|
|
243
|
+
- run: uvx pyossmtool install
|
|
244
|
+
- run: uvx pyossmtool check
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
For GitHub PR annotations, set:
|
|
248
|
+
|
|
249
|
+
```yaml
|
|
250
|
+
error-format: github
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Contributing
|
|
254
|
+
|
|
255
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for local development, adding tools/checks, and release notes.
|
|
256
|
+
|
|
257
|
+
## License
|
|
258
|
+
|
|
259
|
+
MIT
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# pyossmtool
|
|
2
|
+
|
|
3
|
+
Quality gates for Python repos that developers can run locally, in CI, or hand to an AI agent without inventing a new workflow each time.
|
|
4
|
+
|
|
5
|
+
`pyossmtool` gives you one project config, a bundled catalog of checks, quiet success, and structured failure reports. It is inspired by Trunk and pre-commit, but keeps the surface area small:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pyossmtool install
|
|
9
|
+
pyossmtool format
|
|
10
|
+
pyossmtool check
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install pyossmtool
|
|
17
|
+
# or
|
|
18
|
+
uv add --dev pyossmtool
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Requires Python 3.11-3.13.
|
|
22
|
+
|
|
23
|
+
## 60-Second Setup
|
|
24
|
+
|
|
25
|
+
Create `pyossmtool.yaml`:
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
suite: all
|
|
29
|
+
env: auto
|
|
30
|
+
target: .
|
|
31
|
+
error-format: compact
|
|
32
|
+
configs:
|
|
33
|
+
mode: auto
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then run:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pyossmtool install # install tools needed by suite:
|
|
40
|
+
pyossmtool format # apply formatter/autofix checks
|
|
41
|
+
pyossmtool check # report-only quality checks
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The `suite:` value is the project default. You do not need to repeat `--suite all`; the CLI reads it from `pyossmtool.yaml`.
|
|
45
|
+
|
|
46
|
+
Success is silent and exits `0`. Failures exit `1`, write a JSON report under `reports/failures/`, and print the same report through your configured `error-format`.
|
|
47
|
+
|
|
48
|
+
## Mental Model
|
|
49
|
+
|
|
50
|
+
- **Suite**: a named checklist, such as `python-quality`, `standard`, or `all`.
|
|
51
|
+
- **Check**: one rule runner inside a suite, such as `ruff.lint` or `ty.check`.
|
|
52
|
+
- **`check`**: report-only. It should not rewrite your files.
|
|
53
|
+
- **`format`**: applies write/fix checks, such as formatters.
|
|
54
|
+
- **`install`**: installs the tools needed by the selected suite.
|
|
55
|
+
|
|
56
|
+
Most teams pick a suite once in `pyossmtool.yaml` and run the same three commands everywhere.
|
|
57
|
+
|
|
58
|
+
## Config That Matters
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
suite: python-quality # default checklist for install/check/format
|
|
62
|
+
env: auto # auto | managed | project
|
|
63
|
+
target: . # scan root; defaults to .
|
|
64
|
+
error-format: compact # json | log | text | compact | github
|
|
65
|
+
|
|
66
|
+
configs:
|
|
67
|
+
mode: auto # repo configs first, bundled fallback
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Use `--suite` only when you want a one-off override:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pyossmtool check --suite extended
|
|
74
|
+
pyossmtool install --suite standard
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Copy [`pyossmtool.yaml.example`](pyossmtool.yaml.example) for an annotated config with ignores, custom checks, gates, and custom error formatters.
|
|
78
|
+
|
|
79
|
+
## Suites
|
|
80
|
+
|
|
81
|
+
Suites are bundled starting points. They choose which checks run; tools still discover their own files and `.gitignore` is always respected.
|
|
82
|
+
|
|
83
|
+
| Suite | Use it for |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| `all` | Run every bundled check |
|
|
86
|
+
| `python-quality` | Core Python lint, format-check, and type checks |
|
|
87
|
+
| `formatting` | Report-only formatting drift |
|
|
88
|
+
| `format` | Formatter/autofix checks that write files |
|
|
89
|
+
| `standard` | Broader portable quality baseline |
|
|
90
|
+
| `extended` | Slower optional checks on top of standard |
|
|
91
|
+
| `policy` | Bundled script gates |
|
|
92
|
+
| `demo` | Internal/sample fixture suite |
|
|
93
|
+
|
|
94
|
+
Examples:
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
# Local Python repo
|
|
98
|
+
suite: python-quality
|
|
99
|
+
error-format: text
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
# CI baseline with GitHub annotations
|
|
104
|
+
suite: standard
|
|
105
|
+
error-format: github
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
# Max coverage while developing pyossmtool itself
|
|
110
|
+
suite: all
|
|
111
|
+
error-format: compact
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Error Output
|
|
115
|
+
|
|
116
|
+
On failure, pyossmtool always writes the canonical JSON report to disk:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
reports/failures/ruff.lint-20260716T163000Z/report.json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`error-format` only controls what is printed to stderr.
|
|
123
|
+
|
|
124
|
+
| Format | Example output |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `json` | Pretty JSON report, including `report_path` |
|
|
127
|
+
| `log` | `2026-07-16T12:00:00+00:00 [error] ruff.lint/E501 src/app.py:42: Line too long` |
|
|
128
|
+
| `text` | `- [error] E501: Line too long (src/app.py:42)` |
|
|
129
|
+
| `compact` | `src/app.py:42: error: E501 Line too long` |
|
|
130
|
+
| `github` | `::error file=src/app.py,title=ruff.lint/E501,line=42::Line too long` |
|
|
131
|
+
|
|
132
|
+
Default is `json` when `error-format` is omitted.
|
|
133
|
+
|
|
134
|
+
Custom formatters live in `pyossmtool.yaml`:
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
suite: all
|
|
138
|
+
error-format: short
|
|
139
|
+
error-formatters:
|
|
140
|
+
short:
|
|
141
|
+
kind: finding_line
|
|
142
|
+
template: "{severity}\t{rule_id}\t{file}:{line}\t{message}"
|
|
143
|
+
jq-summary:
|
|
144
|
+
kind: jq
|
|
145
|
+
program: >
|
|
146
|
+
.findings[] | "[\(.severity)] \(.rule_id) \(.message)"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`finding_line` placeholders: `severity`, `rule_id`, `message`, `file`, `line`, `check_id`, `report_path`.
|
|
150
|
+
|
|
151
|
+
`jq` formatters require `jq` on `PATH`.
|
|
152
|
+
|
|
153
|
+
## Daily Commands
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
pyossmtool install
|
|
157
|
+
pyossmtool format
|
|
158
|
+
pyossmtool check
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Inspect what is available:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
pyossmtool list suites
|
|
165
|
+
pyossmtool list tools
|
|
166
|
+
pyossmtool list checks
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Run a single check:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
pyossmtool check --check ruff.lint --target .
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Export the failure report schema:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
pyossmtool schema > failure-report.schema.json
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Project-Local Gates
|
|
182
|
+
|
|
183
|
+
Use gates when a repo needs a policy that is not covered by the bundled catalog.
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
pyossmtool gate init module-size --description "Cap module line counts"
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
That creates a shell gate under `.pyossmtool/gates/` and a catalog entry under `.pyossmtool/catalog/checks/`. Enable it from `pyossmtool.yaml`:
|
|
190
|
+
|
|
191
|
+
```yaml
|
|
192
|
+
checks:
|
|
193
|
+
- id: gate.module-size
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## CI
|
|
197
|
+
|
|
198
|
+
Minimal GitHub Actions job:
|
|
199
|
+
|
|
200
|
+
```yaml
|
|
201
|
+
name: quality
|
|
202
|
+
|
|
203
|
+
on:
|
|
204
|
+
pull_request:
|
|
205
|
+
push:
|
|
206
|
+
branches: [main]
|
|
207
|
+
|
|
208
|
+
jobs:
|
|
209
|
+
pyossmtool:
|
|
210
|
+
runs-on: ubuntu-latest
|
|
211
|
+
steps:
|
|
212
|
+
- uses: actions/checkout@v4
|
|
213
|
+
- uses: astral-sh/setup-uv@v6
|
|
214
|
+
- run: uvx pyossmtool install
|
|
215
|
+
- run: uvx pyossmtool check
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
For GitHub PR annotations, set:
|
|
219
|
+
|
|
220
|
+
```yaml
|
|
221
|
+
error-format: github
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Contributing
|
|
225
|
+
|
|
226
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for local development, adding tools/checks, and release notes.
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
id: all
|
|
2
|
+
name: All Checks
|
|
3
|
+
description: Full catalog — every bundled check (check-mode and format-apply). Use
|
|
4
|
+
pyossmtool check for report-only checks and pyossmtool format for apply checks.
|
|
5
|
+
env: auto
|
|
6
|
+
target: .
|
|
7
|
+
ignore-profile:
|
|
8
|
+
- .gitignore
|
|
9
|
+
- .cursorignore
|
|
10
|
+
ignore-paths:
|
|
11
|
+
- .pyossmtool/
|
|
12
|
+
- venv/
|
|
13
|
+
- .venv/
|
|
14
|
+
- reports/
|
|
15
|
+
- sample_files/
|
|
16
|
+
checks:
|
|
17
|
+
- id: ruff.format
|
|
18
|
+
- id: ruff.lint
|
|
19
|
+
- id: ruff.unused
|
|
20
|
+
- id: ty.check
|
|
21
|
+
- id: bandit.scan
|
|
22
|
+
- id: gitleaks.secrets
|
|
23
|
+
- id: semgrep.scan
|
|
24
|
+
- id: deadcode.scan
|
|
25
|
+
- id: vulture.scan
|
|
26
|
+
- id: pydeps.cycles
|
|
27
|
+
- id: radon.cc
|
|
28
|
+
- id: radon.mi
|
|
29
|
+
- id: jscpd.duplication
|
|
30
|
+
- id: shfmt.format
|
|
31
|
+
- id: shellcheck
|
|
32
|
+
- id: mdformat.format
|
|
33
|
+
- id: markdownlint.check
|
|
34
|
+
- id: yamlfmt.format
|
|
35
|
+
- id: yamllint.check
|
|
36
|
+
- id: codespell.spelling
|
|
37
|
+
- id: pytest.test
|
|
38
|
+
target: tests/
|
|
39
|
+
- id: pytest.coverage
|
|
40
|
+
target: tests/
|
|
41
|
+
- id: hadolint.dockerfile
|
|
42
|
+
- id: mutmut.run
|
|
43
|
+
- id: sourcery.review
|
|
44
|
+
- id: gate.module-size
|
|
45
|
+
- id: gate.module-private-vars
|
|
46
|
+
- id: gate.folder-breadth
|
|
47
|
+
- id: gate.acronym-allowlist
|
|
48
|
+
- id: ruff.format.apply
|
|
49
|
+
- id: shfmt.apply
|
|
50
|
+
- id: mdformat.apply
|
|
51
|
+
- id: yamlfmt.apply
|
|
@@ -7,9 +7,10 @@ from pathlib import Path
|
|
|
7
7
|
|
|
8
8
|
import typer
|
|
9
9
|
|
|
10
|
+
from pyossmtool.error_formatters import ErrorFormatter, format_report_file, resolve_error_formatter
|
|
10
11
|
from pyossmtool.gates import lib_path, scaffold_gate
|
|
11
12
|
from pyossmtool.installer import Installer
|
|
12
|
-
from pyossmtool.models import CheckMode, FailureReport
|
|
13
|
+
from pyossmtool.models import CheckMode, FailureReport, ProjectConfig
|
|
13
14
|
from pyossmtool.registry import Registry
|
|
14
15
|
from pyossmtool.runner import Runner
|
|
15
16
|
|
|
@@ -88,12 +89,22 @@ def export_schema() -> None:
|
|
|
88
89
|
|
|
89
90
|
@app.command("install")
|
|
90
91
|
def install(
|
|
91
|
-
suite: str = typer.Option(
|
|
92
|
+
suite: str | None = typer.Option(
|
|
93
|
+
None,
|
|
94
|
+
"--suite",
|
|
95
|
+
help="Suite whose tools to install (default: suite from pyossmtool.yaml)",
|
|
96
|
+
),
|
|
92
97
|
) -> None:
|
|
93
98
|
registry = _registry()
|
|
94
|
-
|
|
99
|
+
project_root = _project_root()
|
|
100
|
+
project_config = registry.load_project_config(project_root)
|
|
101
|
+
suite_id = suite or (project_config.suite if project_config else None)
|
|
102
|
+
if not suite_id:
|
|
103
|
+
typer.echo("SETUP provide --suite or set suite: in pyossmtool.yaml", err=True)
|
|
104
|
+
raise typer.Exit(code=2)
|
|
105
|
+
installer = Installer(registry, project_root)
|
|
95
106
|
try:
|
|
96
|
-
installer.install_suite(
|
|
107
|
+
installer.install_suite(suite_id)
|
|
97
108
|
except Exception as exc:
|
|
98
109
|
typer.echo(f"SETUP {exc}", err=True)
|
|
99
110
|
raise typer.Exit(code=2) from exc
|
|
@@ -101,7 +112,11 @@ def install(
|
|
|
101
112
|
|
|
102
113
|
@app.command("check")
|
|
103
114
|
def check_cmd(
|
|
104
|
-
suite: str | None = typer.Option(
|
|
115
|
+
suite: str | None = typer.Option(
|
|
116
|
+
None,
|
|
117
|
+
"--suite",
|
|
118
|
+
help="Suite id to run (default: suite from pyossmtool.yaml)",
|
|
119
|
+
),
|
|
105
120
|
check: str | None = typer.Option(None, "--check", help="Single check id to run"),
|
|
106
121
|
target: str | None = typer.Option(None, "--target", help="Target path for single check"),
|
|
107
122
|
fail_fast: bool = typer.Option(False, "--fail-fast", help="Stop at first failure"),
|
|
@@ -113,7 +128,11 @@ def check_cmd(
|
|
|
113
128
|
|
|
114
129
|
@app.command("format")
|
|
115
130
|
def format_cmd(
|
|
116
|
-
suite: str | None = typer.Option(
|
|
131
|
+
suite: str | None = typer.Option(
|
|
132
|
+
None,
|
|
133
|
+
"--suite",
|
|
134
|
+
help="Suite id to run (default: suite from pyossmtool.yaml)",
|
|
135
|
+
),
|
|
117
136
|
check: str | None = typer.Option(None, "--check", help="Single check id to run"),
|
|
118
137
|
target: str | None = typer.Option(None, "--target", help="Target path for single check"),
|
|
119
138
|
fail_fast: bool = typer.Option(False, "--fail-fast", help="Stop at first failure"),
|
|
@@ -134,14 +153,19 @@ def _execute(
|
|
|
134
153
|
registry = _registry()
|
|
135
154
|
project_root = _project_root()
|
|
136
155
|
project_config = registry.load_project_config(project_root)
|
|
156
|
+
try:
|
|
157
|
+
formatter = resolve_error_formatter(project_config)
|
|
158
|
+
except (KeyError, ValueError, RuntimeError) as exc:
|
|
159
|
+
typer.echo(f"SETUP {exc}", err=True)
|
|
160
|
+
return 2
|
|
137
161
|
runner = Runner(registry, project_root, verbose=verbose)
|
|
138
162
|
|
|
139
163
|
if check:
|
|
140
|
-
return _run_single_check(runner, check, target, suite, project_config, mode)
|
|
164
|
+
return _run_single_check(runner, check, target, suite, project_config, mode, formatter)
|
|
141
165
|
|
|
142
166
|
suite_id = suite or (project_config.suite if project_config else None)
|
|
143
167
|
if not suite_id:
|
|
144
|
-
typer.echo("SETUP provide --suite or
|
|
168
|
+
typer.echo("SETUP provide --suite or set suite: in pyossmtool.yaml", err=True)
|
|
145
169
|
return 2
|
|
146
170
|
|
|
147
171
|
try:
|
|
@@ -154,7 +178,7 @@ def _execute(
|
|
|
154
178
|
except KeyError as exc:
|
|
155
179
|
typer.echo(f"SETUP {exc}", err=True)
|
|
156
180
|
return 2
|
|
157
|
-
return _emit_suite_result(suite_result)
|
|
181
|
+
return _emit_suite_result(suite_result, formatter)
|
|
158
182
|
|
|
159
183
|
|
|
160
184
|
def _run_single_check(
|
|
@@ -162,8 +186,9 @@ def _run_single_check(
|
|
|
162
186
|
check: str,
|
|
163
187
|
target: str | None,
|
|
164
188
|
suite: str | None,
|
|
165
|
-
project_config,
|
|
189
|
+
project_config: ProjectConfig | None,
|
|
166
190
|
mode: CheckMode,
|
|
191
|
+
formatter: ErrorFormatter,
|
|
167
192
|
) -> int:
|
|
168
193
|
if not target:
|
|
169
194
|
typer.echo("SETUP --target is required with --check", err=True)
|
|
@@ -181,35 +206,42 @@ def _run_single_check(
|
|
|
181
206
|
suite_id=suite or (project_config.suite if project_config else None),
|
|
182
207
|
project_config=project_config,
|
|
183
208
|
)
|
|
184
|
-
return _exit_for_check_result(result)
|
|
209
|
+
return _exit_for_check_result(result, formatter)
|
|
185
210
|
|
|
186
211
|
|
|
187
|
-
def _exit_for_check_result(result) -> int:
|
|
212
|
+
def _exit_for_check_result(result, formatter: ErrorFormatter) -> int:
|
|
188
213
|
if result.error:
|
|
189
214
|
typer.echo(f"SETUP {result.error}", err=True)
|
|
190
215
|
return 2
|
|
191
216
|
if result.passed:
|
|
192
217
|
return 0
|
|
193
|
-
|
|
218
|
+
_emit_failed_check(result, formatter)
|
|
194
219
|
return 1
|
|
195
220
|
|
|
196
221
|
|
|
197
|
-
def _emit_suite_result(suite_result) -> int:
|
|
222
|
+
def _emit_suite_result(suite_result, formatter: ErrorFormatter) -> int:
|
|
198
223
|
if suite_result.passed:
|
|
199
224
|
return 0
|
|
200
225
|
for result in suite_result.results:
|
|
201
|
-
_emit_failed_check(result)
|
|
226
|
+
_emit_failed_check(result, formatter)
|
|
202
227
|
return 1
|
|
203
228
|
|
|
204
229
|
|
|
205
|
-
def _emit_failed_check(result) -> None:
|
|
230
|
+
def _emit_failed_check(result, formatter: ErrorFormatter) -> None:
|
|
206
231
|
if result.passed:
|
|
207
232
|
return
|
|
208
233
|
if result.error:
|
|
209
234
|
typer.echo(f"SETUP {result.check_id}: {result.error}", err=True)
|
|
210
235
|
return
|
|
211
|
-
if result.report_path:
|
|
236
|
+
if not result.report_path:
|
|
237
|
+
return
|
|
238
|
+
try:
|
|
239
|
+
text = format_report_file(result.report_path, formatter)
|
|
240
|
+
except (OSError, ValueError, RuntimeError) as exc:
|
|
241
|
+
typer.echo(f"SETUP failed to format report for {result.check_id}: {exc}", err=True)
|
|
212
242
|
typer.echo(f"FAIL {result.check_id} -> {result.report_path}", err=True)
|
|
243
|
+
return
|
|
244
|
+
typer.echo(text, err=True)
|
|
213
245
|
|
|
214
246
|
|
|
215
247
|
def main() -> None:
|