mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
302 lines
14 KiB
TOML
302 lines
14 KiB
TOML
[build-system]
|
||
requires = ["setuptools>=68", "wheel"]
|
||
build-backend = "setuptools.build_meta"
|
||
|
||
[project]
|
||
name = "infinito-nexus"
|
||
version = "16.2.2"
|
||
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",
|
||
]
|