Files
computer-playbook/pyproject.toml
T

302 lines
14 KiB
TOML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "infinito-nexus"
version = "16.2.1"
description = "Infinito.Nexus"
readme = "README.md"
requires-python = ">=3.11"
license = { file = "LICENSE.md" }
dependencies = [
# Pinned: utils/recovery reads the generation layout from baudolo's own
# BackupPaths, so the version here and in svc-bkp-volume-2-local must match.
"backup-docker-to-local==7.1.1",
"bcrypt",
"cryptography",
"humanfriendly",
"PyMySQL",
"psycopg2-binary",
"ldap3",
"PyYAML",
"ruamel.yaml",
]
[project.optional-dependencies]
deploy = [
"ansible",
"colorscheme-generator @ https://github.com/kevinveenbirkenbach/colorscheme-generator/archive/refs/tags/v0.3.0.zip",
"tld",
"passlib",
"requests",
]
dev = [
"ruff==0.17.1",
"import-linter",
"pre-commit",
"git-maintainer-tools",
"pytest>=9.1",
"pytest-xdist",
]
fuzz = [
"atheris==3.1.0",
]
[project.scripts]
infinito = "cli.__main__:main"
[tool.setuptools]
packages = { find = { where = ["."], include = [
"cli*",
"plugins*",
"utils*",
"library*",
], exclude = [
"roles*",
"assets*",
"docs*",
"templates*",
"logs*",
"tasks*",
"tests*",
"__pycache__*",
] } }
include-package-data = true
[tool.ruff]
respect-gitignore = true
target-version = "py311"
exclude = [
"build",
"dist",
"*.egg-info",
".venv",
"venv",
]
[tool.ruff.lint]
# Default ruff is `E4 + E7 + E9 + F`. We add:
# - `I` (isort) — import order, auto-fixable
# - `B` (flake8-bugbear) — likely-bug patterns (mutable defaults, etc.)
# - `UP` (pyupgrade) — modernise syntax to the target_version above
# - `RUF` (ruff-specific) — unused-noqa, ambiguous unicode, mutable class attrs, …
# - `SIM` (flake8-simplify) — `if x: True else: False` → `bool(x)`, etc.
# - `C4` (flake8-comprehensions) — `list(x for x in y)` → `[x for x in y]`, …
# - `PERF` (perflint) — avoid try/except in hot loops, etc.
# - `RET` (flake8-return) — unneeded `else` after `return`, redundant assigns
# - `PIE` (flake8-pie) — small wins (no-op `pass`, redundant `dict()` calls)
# - `T10` (flake8-debugger) — committed `breakpoint()` / `pdb` calls
# - `PGH` (pygrep-hooks) — blanket `# noqa` / `# type: ignore` without code
# - `EXE` (flake8-executable) — shebang/executable-bit consistency
# - `RSE` (flake8-raise) — `raise X()` style
# - `ICN` (flake8-import-conventions) — standard import aliases (`numpy as np`)
# - `DTZ` (flake8-datetimez) — tz-naive `datetime.now()` / `utcnow()` bugs
# - `TID` (flake8-tidy-imports) — banned modules / banned relative imports
# - `LOG` (flake8-logging) — `logging` module misuse
# - `G` (flake8-logging-format) — f-strings inside `logging.x()` calls (defeats lazy-format)
# - `S` (flake8-bandit) — security (hardcoded creds, weak hashes, subprocess(shell=True), …)
# - `PTH` (flake8-use-pathlib) — modernise `os.path.*` calls to `pathlib.Path`
# - `FURB` (refurb) — modern Python idioms beyond UP/SIM/PIE/PERF
# - `W` (pycodestyle warnings) — natural pair with `E`; trailing whitespace etc.
# - `FA` (flake8-future-annotations) — consistent `from __future__ import annotations`
# - `YTT` (flake8-2020) — correct `sys.version` / `sys.version_info` checks
# - `A` (flake8-builtins) — shadowing built-in names
# - `ISC` (flake8-implicit-str-concat) — accidental `"foo" "bar"` typos
# - `SLOT` (flake8-slots) — require `__slots__` on `str`/`tuple`/`namedtuple` subclasses
# - `FLY` (flynt) — `"{}".format(x)` → f-string
# - `PYI` (flake8-pyi) — stub-hygiene rules (also fire on `.py`)
# - `TC` (flake8-type-checking) — runtime-only imports out of `if TYPE_CHECKING:` blocks
# - `N` (pep8-naming) — class/function/variable naming consistency
# - `PLE0605` — `__all__` must be a `list`/`tuple` (not a single str)
# - `PLW1510` — `subprocess.run(...)` without `check=` swallows non-zero exits
# - `PLW2901` — loop variable rebound inside the body (typo/bug class)
# - `PLW0108` — `lambda x: f(x)` → just `f`
# - `PLW0603` — `global` statement (code smell)
# - `PLR5501` — `else: if …` → `elif` (auto-fixable)
# - `PLC0207` — `.split()`/`.rsplit()` should use `maxsplit=` when only the first/last segment is used
# - `PLR1722` — `sys.exit(0)` over `exit(0)` (interpreter-only alias)
# - `PLR1714` — collapse repeated `x == "a" or x == "b"` into `x in {"a", "b"}`
# - `TRY002` — don't `raise Exception(...)` (use a specific class)
# - `TRY004` — type-check failures must `raise TypeError`, not `ValueError`
# - `TRY300` — put the success-path under `else:` so `except:` only sees the dangerous bit
# - `TRY301` — don't `raise` inside `try:` for an exception caught by the same `except:`
# - `BLE001` (flake8-blind-except) — catch-all `except Exception:`/bare `except:` (ratchet; see per-file-ignores)
select = [
"E", "F", "I", "B", "UP", "RUF", "SIM", "C4", "PERF", "RET", "PIE",
"T10", "PGH", "EXE", "RSE", "ICN", "DTZ",
"TID", "LOG", "G",
"S",
"PTH",
"FURB", "W", "FA", "YTT", "A", "ISC", "SLOT", "FLY",
"PYI",
"TC", "N",
"PLE0605",
"PLW1510", "PLW2901", "PLW0108", "PLW0603",
"PLR5501", "PLC0207", "PLR1722", "PLR1714",
"TRY002", "TRY004", "TRY300", "TRY301",
"BLE001",
]
# `ruff format` (run via `make autoformat`) reflows whatever it can.
# E501 leftovers are content the formatter can't break (long string
# literals, URLs / shell commands inside f-strings, comments). Trust
# the formatter; don't fail CI on the unsplittable rest.
#
# RUF001/002/003 (ambiguous unicode in strings / docstrings / comments)
# exist to catch homoglyph attacks, not English typography. The project
# uses em/en-dashes deliberately and `ℹ` as a UI-hint glyph in CLI help
# output; flagging those is noise.
ignore = [
"E501",
"RUF001", "RUF002", "RUF003",
# S603 (subprocess call without `shell=False`) and S607 (start
# process with partial path) are over-eager for a project whose
# CLI invokes `make` / `git` / `docker` / `ansible-playbook` from
# PATH using list-form args. The list-form already prevents shell
# injection (Python's `subprocess` does not parse argv[0] as a
# shell command), and resolving every binary's absolute path in
# the source would couple the code to the host filesystem layout.
# The genuinely dangerous shell-injection patterns (`shell=True`)
# are still caught by S602/S605.
"S603", "S607",
# PTH207 (`glob.glob` → `Path.glob`/`rglob`) and PTH208
# (`os.listdir` → `Path.iterdir`) change the return shape:
# `list[str]` → iterator of `Path`. Every call site needs per-site
# review of how the result is consumed (string-path arithmetic,
# `.startswith` / `.endswith` checks, plain string comparisons,
# downstream APIs that require `str`). That work belongs to a
# separate "iter-modernisation" follow-up commit, not this
# use-pathlib pass.
"PTH207", "PTH208",
# SIM112 (capitalise env var names) conflicts with the deliberate
# lowercase routing-knob surface: `make compose-deploy disable=x`
# sets shell env `disable` via GNU Make's auto-export, and the
# consumers read the same name. Forcing uppercase would split the
# surface (`make … disable=x` writes lowercase, code reads
# `DISABLE`) and break the make/env name parity.
"SIM112",
]
[tool.ruff.lint.per-file-ignores]
# Test code legitimately uses patterns flake8-bandit flags as security
# concerns in production code:
# - S101 (`assert`) is the unittest idiom.
# - S102 (`exec`) is used to load script-under-test by file path.
# - S105/S106 (hardcoded passwords) appear in fixtures / dummy creds.
# - S108 (hardcoded /tmp paths) appears in tempdir fixtures.
# - S110/S112 (try/except pass/continue) appear in cleanup helpers.
# - BLE001 (broad `except Exception`) is idiomatic in test fixtures /
# cleanup helpers that must never abort the suite on teardown.
"tests/**" = ["S101", "S102", "S105", "S106", "S108", "S110", "S112", "BLE001"]
# Standalone scripts under ``roles/*/files/`` and ``utils/cert_utils.py``
# are exercised by test fixtures that monkey-patch the legacy ``os.*``
# surface (``os.makedirs``, ``os.stat`` with non-pathlib kwargs,
# ``patch.object(sut, "os")`` replacements). Converting these call
# sites to ``pathlib.Path`` would require rewriting every coupled
# test in lockstep — out of scope for the use-pathlib pass.
"roles/sys-ctl-rpr-container-hard/files/python/script.py" = ["PTH"]
"roles/sys-ctl-rpr-container-soft/files/python/script.py" = ["PTH"]
# BLE001 (flake8-blind-except) ratchet.
# Broad `except Exception:` is now selected project-wide so NEW code is
# gated. Standalone role scripts under ``roles/*/files/`` legitimately
# swallow-and-continue for resilience, so the whole tree is exempt. The
# production modules below predate the rule and are grandfathered pending
# a focused burn-down; they should shrink, not grow. New production files
# are gated. CodeQL (`py/empty-except`) still covers the empty-body case
# everywhere, including the exempt paths.
"roles/**" = ["BLE001"]
"cli/administration/deploy/container/command.py" = ["BLE001"]
"cli/administration/deploy/dedicated/apps.py" = ["BLE001"]
"cli/administration/deploy/dedicated/command.py" = ["BLE001"]
"cli/administration/deploy/development/inventory/legacy_resolver.py" = ["BLE001"]
"cli/administration/deploy/development/proc.py" = ["BLE001"]
"cli/administration/inventory/credentials/vault.py" = ["BLE001"]
"cli/administration/inventory/devices/command.py" = ["BLE001"]
"cli/administration/inventory/provision/cli.py" = ["BLE001"]
"cli/administration/inventory/provision/credentials_generator.py" = ["BLE001"]
"cli/administration/inventory/provision/host_vars.py" = ["BLE001"]
"cli/administration/inventory/provision/services_disabler.py" = ["BLE001"]
"cli/administration/inventory/validate/loaders.py" = ["BLE001"]
"cli/contributing/mirror/sync/__main__.py" = ["BLE001"]
"cli/core/help.py" = ["BLE001"]
"cli/meta/ci/roles/runtime/__main__.py" = ["BLE001"]
"cli/meta/j2/compiler/__main__.py" = ["BLE001"]
"cli/meta/roles/applications/all/__main__.py" = ["BLE001"]
"cli/meta/roles/applications/in_group_deps/__main__.py" = ["BLE001"]
"cli/meta/roles/applications/invokable/__main__.py" = ["BLE001"]
"cli/meta/roles/applications/type/__main__.py" = ["BLE001"]
"cli/meta/roles/lifecycle/__main__.py" = ["BLE001"]
"cli/meta/roles/services/called/__init__.py" = ["BLE001"]
"library/docker_bridge_gateway.py" = ["BLE001"]
"plugins/filter/active_docker.py" = ["BLE001"]
"plugins/filter/docker/service_enabled.py" = ["BLE001"]
"plugins/filter/dotenv.py" = ["BLE001"]
"plugins/filter/get/all/application_ids.py" = ["BLE001"]
"plugins/filter/role_path_by_app_id.py" = ["BLE001"]
"plugins/filter/split_postgres_connections.py" = ["BLE001"]
"plugins/lookup/addon_env_flags.py" = ["BLE001"]
"plugins/lookup/applications_current_play.py" = ["BLE001"]
"plugins/lookup/image.py" = ["BLE001"]
"plugins/lookup/email.py" = ["BLE001"]
"plugins/lookup/prometheus_integration_active.py" = ["BLE001"]
"plugins/lookup/version.py" = ["BLE001"]
"utils/cache/base.py" = ["BLE001"]
"utils/nexus/list_apt_proxy_repos.py" = ["BLE001"]
"utils/roles/applications/in_group_deps.py" = ["BLE001"]
"utils/roles/applications/services/resources.py" = ["BLE001"]
"utils/roles/dependency_resolver.py" = ["BLE001"]
"utils/roles/validation/invokable.py" = ["BLE001"]
"utils/roles/validation/resources.py" = ["BLE001"]
"utils/templating/ansible.py" = ["BLE001"]
"utils/cert_utils.py" = ["PTH", "BLE001"]
# N999 (invalid module name) on Ansible-invoked role-level scripts
# whose filenames contain hyphens. Renaming to snake_case would break
# the Ansible meta references that source them via the literal path,
# so the hyphenated filename is locked in by the surrounding stack.
# `include_r` would be the canonical lowercase form per N806, but the
# matrix-deploy code consistently writes the round index as ``R`` in
# its formulas (``R if R < count else 0``) and the variable name
# ``include_R`` reads as "round R's include set" right next to those
# formulas. The uppercase R here is documentation, not a constant.
"cli/administration/deploy/development/deploy.py" = ["N806"]
"cli/administration/deploy/development/init.py" = ["N806"]
"cli/administration/deploy/development/inventory.py" = ["N806"]
"tests/unit/python/cli/administration/deploy/development/test_inventory.py" = ["N806"]
# Lazy-loaded class binding: ``ValueGenerator = _value_generator_cls()``
# rebinds the imported class inside the function. PEP 8 keeps the
# UpperCamelCase form for class identifiers, including local re-binds.
"plugins/filter/value_generator.py" = ["N806"]
# ---------------------------------------------------------------------------
# import-linter — architectural import contracts (run via `lint-imports`,
# enforced by tests/lint/filesystem/python/test_import_contracts.py).
# ---------------------------------------------------------------------------
[tool.importlinter]
root_package = "cli"
[[tool.importlinter.contracts]]
# The dev/CI deploy package was repeatedly hit by CodeQL
# `py/unsafe-cyclic-import`. The fix split the env/arg helpers into the
# leaf module `env`, giving the load-time DAG compose -> common -> env.
# This layers contract locks that order in: a lower layer importing a
# higher one (i.e. a new cycle) breaks the build.
name = "cli.administration.deploy.development stays acyclic (compose -> common -> env)"
type = "layers"
layers = [
"cli.administration.deploy.development.compose",
"cli.administration.deploy.development.common",
"cli.administration.deploy.development.env",
]
# Type-checking-only / lazily-deferred back-references to `compose` do
# not run at import time, so they cannot form a load-time cycle.
ignore_imports = [
"cli.administration.deploy.development.common -> cli.administration.deploy.development.compose",
]