[build-system] requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [project] name = "devx" dynamic = ["version"] description = "Reusable development and CI/CD tools for oblachno-oss projects" readme = "README.md" license = {text = "GPL-3.0"} requires-python = ">=3.12" classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", ] # All dependencies are pinned to exact versions for full reproducibility. # Update pinned versions in a dedicated PR with verification. dependencies = [ "requests==2.34.2", "python-dotenv==1.2.2", "click==8.4.2", "tenacity==9.1.4", # retry logic for GiteaClient/VikunjaClient "jinja2==3.1.6", # template rendering (devx.utils.jinja, check_alert_rules) "pyyaml==6.0.3", # YAML parsing (workflow checks, ansible checks) ] [project.scripts] devx = "devx.cli:cli" # Pytest plugin — auto-discovered by pytest when devx is installed. # Runs static analysis on test files during every pytest invocation # to detect un-hermetic patterns (unpatched subprocess, time.sleep, etc.) [project.entry-points.pytest11] devx_test_isolation = "devx.tools.check_test_isolation" [tool.setuptools.dynamic] version = {attr = "devx.__version__"} [project.optional-dependencies] # Test runners (pytest + coverage + parallel execution) ci = [ "pytest==9.1.1", "pytest-cov==7.1.0", "pytest-xdist==3.8.0", ] # Lint and type-checking tools (quality job, badge generation) lint = [ "ruff==0.15.21", "pyright==1.1.411", "bandit==1.9.4", "pip-audit==2.10.1", "pre-commit==4.6.0", ] # Release tools (build + publish to PyPI/Gitea registry) release = [ "build==1.5.1", "twine==6.2.0", ] # Molecule testing (for projects with Ansible roles) molecule = [ "molecule==26.6.0", "molecule-docker==2.1.0", "ansible-lint==26.6.0", "ansible-core==2.21.1", ] # Deploy tools (for infra staging/production deployments) # Versions aligned with infra's pyproject.toml to avoid reinstalls on every CI job. # bcrypt and PyJWT are infra deps not in devx core — included here so the CI # image has them and setup-image can use --no-deps (skip dep resolution). deploy = [ "ansible-core==2.21.1", "boto3==1.43.44", "docker==7.1.0", "cryptography==50.0.0", "bcrypt==5.0.0", "PyJWT==2.13.0", ] # Full dev environment (local development) dev = [ "devx[ci,lint,release,molecule]", "build==1.5.1", "twine==6.2.0", ] [tool.setuptools.packages.find] where = ["src"] [tool.setuptools.package-data] devx = ["translations.json", "make/*.mak"] [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["src"] addopts = "--cov=src/devx --cov-report=term-missing --cov-fail-under=100 -p no:devx_test_isolation" markers = [ "integration: marks tests as integration tests (not counted in coverage)", ] [tool.coverage.run] # The test isolation pytest plugin (check_test_isolation.py) is loaded # by pytest before coverage instrumentation starts. Coverage config below # excludes decorator lines and pragma-marked code from the coverage check. branch = false [tool.coverage.report] exclude_lines = [ "pragma: no cover", "if __name__ == .__main__", # Click decorator lines are executed at import time, before coverage "@click\\.command|@click\\.option|@click\\.argument", ] [tool.ruff] target-version = "py312" line-length = 120 [tool.ruff.lint] select = ["E", "F", "I", "W", "UP", "B", "C4", "SIM"] ignore = ["SIM117"] [tool.ruff.format] quote-style = "double" indent-style = "space" [tool.pyright] include = ["src"] pythonVersion = "3.12" venvPath = "." venv = ".venv" strict = ["src/devx/config.py", "src/devx/exceptions.py", "src/devx/i18n.py", "src/devx/api_clients.py", "src/devx/gitea_cli.py"] # --------------------------------------------------------------------------- # Change classification — determines which changes trigger a release # --------------------------------------------------------------------------- # The framework provides DEFAULT_INFRASTRUCTURE (CI workflows, tests, docs, # lint config, etc.) that applies to any Python project. We only specify # what's different about devx. # # Rule priority (first match wins): # 1. user_facing_overrides (safety — highest priority) # 2. infrastructure_overrides (explicit per-file) # Project-specific devx configuration (read by devx.config) [tool.devx] task_prefix = "DEVX" vikunja_project_id = 8 repo_owner = "oblachno-oss" repo_name = "devx" [tool.devx.check_agent_docs] skip_ref_prefixes = [ "src/myproject/", "ansible/requirements.yml", ] # 3. infrastructure (DEFAULT_INFRASTRUCTURE + project-specific patterns) # 4. Default: user-facing (safe) [tool.devx.classify] # use_defaults = true # (default) merge with DEFAULT_INFRASTRUCTURE # Project-specific infrastructure paths (merged with defaults). # devx has no additional infrastructure paths — everything not in the # defaults is user-facing (src/devx/**, pyproject.toml, translations.json). infrastructure = [] # Infrastructure overrides — files that would default to user-facing # but are actually infrastructure: # - __init__.py: only contains __version__ (set by release.py, not user code) # # NOTE: api_clients.py is NOT here — it's used by devx's CI modules # (auto_merge.py, release.py, pr_review.py, etc.) which consumer projects # call via `python -m devx.ci.*`. Changes to api_clients.py affect consumer # projects' CI behavior, so it IS user-facing. infrastructure_overrides = [ "src/devx/__init__.py", ] # User-facing overrides — safety override for broad infrastructure patterns # devx workflow files (.gitea/**) are reference implementations that # downstream repos (grm, infra) copy from. Changes to them affect how # consumer projects run their CI, so they must trigger a release. user_facing_overrides = [ ".gitea/**", ] # Tag patterns — additional categories for CI conditional execution # Orthogonal to release impact (user-facing vs infrastructure) [tool.devx.classify.tags] # No tags needed for devx itself — it has no ansible/ directory