c537073c21
ci / dockerfile (push) Has been cancelled
ci / types (push) Has been cancelled
ci / lint (push) Has been cancelled
ci / security (push) Has been cancelled
ci / chart (push) Has been cancelled
ci / image (api) (push) Has been cancelled
ci / image (reconciler) (push) Has been cancelled
ci / image (worker) (push) Has been cancelled
ci / integration (push) Has been cancelled
ci / unit (push) Has been cancelled
ci / bump (push) Has been cancelled
Python 3.12 -> 3.14, postgres 16 -> 18, uv 0.5.11 -> 0.11.29, trivy 0.58.1 -> 0.72.0, gitleaks 8.21.2 -> 8.30.1, yq 4.44.6 -> 4.53.3, and every action re-pinned to the SHA of its latest tag (checkout v7, setup-uv v8, buildx v4, login v4, hadolint v3.3.0). helm stays 3.21.3: already current for 3.x, and helm 4 is a breaking change, not a CVE fix. trivy mattered most. A vulnerability scanner fourteen minor versions behind is the one stale pin that hides all the others. ruff target-version is deliberately py313 while the runtime is 3.14. It controls the syntax the formatter may emit, and at py314 it rewrites 'except (A, B):' into PEP 758's unparenthesized form — which reads exactly like Python 2's 'except E, name:' and is a hard SyntaxError below 3.14. No semantic gain, real readability cost, in a repo meant to be read. Verified on 3.14: ruff, ruff format, mypy --strict, 166 tests, helm lint, bandit, pip-audit. The digest guard still rejects placeholder digests. Risk carried knowingly: the bumped actions run on node24. If act_runner only provides node20, every job fails at action startup and this commit is the revert.
127 lines
4.4 KiB
TOML
127 lines
4.4 KiB
TOML
[project]
|
|
name = "svcforge"
|
|
version = "0.1.0"
|
|
description = "X-as-a-Service control plane — reference implementation"
|
|
requires-python = ">=3.14"
|
|
dependencies = [
|
|
"svcforge-core",
|
|
"fastapi>=0.115",
|
|
"uvicorn[standard]>=0.32",
|
|
"psycopg[binary,pool]>=3.2",
|
|
"pyjwt[crypto]>=2.9",
|
|
"httpx>=0.27",
|
|
"croniter>=3.0",
|
|
"tzdata>=2024.2",
|
|
"structlog>=24.4",
|
|
"prometheus-client>=0.21",
|
|
"redis>=5.2",
|
|
"typer>=0.15",
|
|
"opentelemetry-api>=1.28",
|
|
"opentelemetry-sdk>=1.28",
|
|
"opentelemetry-instrumentation-fastapi>=0.49b0",
|
|
"opentelemetry-instrumentation-psycopg>=0.49b0",
|
|
]
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"pytest>=8.3",
|
|
"pytest-asyncio>=0.24",
|
|
"pytest-cov>=6.0",
|
|
"mypy>=1.13",
|
|
"ruff>=0.8",
|
|
"hypothesis>=6.122",
|
|
"pre-commit>=4.0",
|
|
"testcontainers[postgres]>=4.9",
|
|
"types-pyyaml>=6.0.12.20260518",
|
|
]
|
|
|
|
[project.scripts]
|
|
svcforge = "services.cli.main:app"
|
|
|
|
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[tool.hatch.build.targets.wheel]
|
|
packages = ["services"]
|
|
|
|
[tool.uv.sources]
|
|
# editable here is a DEVELOPMENT convenience: source edits are visible without a
|
|
# reinstall. The Dockerfiles deliberately override it with `uv sync --no-editable`,
|
|
# because an editable install in an image resolves imports to /app/libs and ships a
|
|
# path, not a package.
|
|
svcforge-core = { path = "libs/svcforge_core", editable = true }
|
|
|
|
[tool.ruff]
|
|
line-length = 110
|
|
# py313, while the project RUNS on 3.14. `target-version` controls the syntax the
|
|
# formatter is allowed to EMIT, and at py314 it rewrites `except (A, B):` into PEP 758's
|
|
# unparenthesized `except A, B:`.
|
|
#
|
|
# That rewrite is rejected here for two reasons. It is visually identical to Python 2's
|
|
# `except E, name:`, which binds a variable rather than catching two types — a reader who
|
|
# learned Python 2, or an LLM trained on it, reads the opposite of what it does. And it
|
|
# makes the source a hard SyntaxError on 3.13 and earlier for no semantic gain, in a repo
|
|
# whose job is to be read.
|
|
#
|
|
# Raise this only for syntax that earns its incompatibility.
|
|
target-version = "py313"
|
|
|
|
[tool.ruff.lint]
|
|
select = ["E", "F", "I", "UP", "B", "ANN", "S", "C4", "RUF"]
|
|
|
|
[tool.ruff.lint.isort]
|
|
# svcforge_core lives under libs/, so isort cannot infer it is ours.
|
|
known-first-party = ["svcforge_core", "services"]
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
# Tests may assert, and fixtures shadow names by design.
|
|
"tests/**" = ["S101"]
|
|
# The e2e tests drive the real `kubectl`/`helm`/`pgrep` off PATH — that is the whole point
|
|
# of them, and pinning absolute paths would make them pass on one machine only. Scoped to
|
|
# this directory so S603/S607 keep guarding the application code, where `team` is tenant
|
|
# input that reaches a helm release name.
|
|
"tests/e2e/**" = ["S101", "S603", "S607"]
|
|
|
|
[tool.bandit]
|
|
# bandit is the belt to ruff's suspenders: ruff's `S` ruleset IS flake8-bandit and runs on
|
|
# every file in the lint stage. These two skips are the rules ruff already enforces here,
|
|
# where each real site carries an individually justified `# noqa` that bandit cannot see —
|
|
# so bandit re-reports them as findings that ruff has already adjudicated.
|
|
#
|
|
# B608 (SQL built by string) == ruff S608. Every occurrence interpolates `_COLUMNS`, a
|
|
# module constant. Tenant input goes through psycopg parameters, never the f-string,
|
|
# and ruff fails the build if that ever changes.
|
|
# B104 (bind 0.0.0.0) == ruff S104. A container must bind all interfaces; the pod's
|
|
# network namespace is the boundary, not the listen address.
|
|
#
|
|
# Nothing else is skipped. If you add a third, justify it here or you are just turning the
|
|
# gate off one rule at a time.
|
|
skips = ["B608", "B104"]
|
|
exclude_dirs = [".venv", "tests"]
|
|
|
|
[tool.mypy]
|
|
strict = true
|
|
python_version = "3.14"
|
|
warn_unreachable = true
|
|
|
|
[[tool.mypy.overrides]]
|
|
module = ["testcontainers.*", "croniter.*"]
|
|
ignore_missing_imports = true
|
|
|
|
# The OTLP exporter is an optional runtime dependency: in the cluster the API runs under
|
|
# `opentelemetry-instrument`, which brings its own. obs.py imports it inside a try/except
|
|
# and degrades to in-process traces without it, so mypy must not require it to be installed.
|
|
[[tool.mypy.overrides]]
|
|
module = ["opentelemetry.exporter.*"]
|
|
ignore_missing_imports = true
|
|
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests"]
|
|
addopts = "-q --strict-markers"
|
|
asyncio_mode = "auto"
|
|
markers = [
|
|
"slow: >1s",
|
|
"e2e: needs a cluster and real helm",
|
|
]
|