Files
svcforge/pyproject.toml
T
Nguyen Minh Phuc 851f8919a8
ci / lint (push) Successful in 35s
ci / unit (push) Successful in 1m11s
ci / types (push) Successful in 1m22s
ci / dockerfile (push) Successful in 4s
ci / security (push) Successful in 1m28s
ci / integration (push) Successful in 1m36s
ci / image (reconciler) (push) Failing after 17m0s
ci / image (api) (push) Failing after 17m1s
ci / image (worker) (push) Failing after 13m8s
ci / bump (push) Has been skipped
ci: fix coverage target, pip-audit scope, bandit config
- --cov pointed at libs/svcforge_core/domain, a path that does not exist (the
  package nests one level deeper). Coverage measured 0.00% of the code. Use the
  module form, which is layout-independent.
- pip-audit --strict cannot audit our own editable, not-on-PyPI packages. Audit
  the locked dependency set instead and keep --strict.
- bandit re-reports B608/B104, which ruff's S ruleset already enforces with
  justified per-line noqa it cannot see. Skipped in config, with reasons.
- registry host was git.oci-oci; it is gitea.oci-oci.
- integration job set SVCFORGE_PG_DSN; conftest reads SVCFORGE_TEST_DSN.
2026-07-17 10:51:59 +00:00

116 lines
3.7 KiB
TOML

[project]
name = "svcforge"
version = "0.1.0"
description = "X-as-a-Service control plane — reference implementation"
requires-python = ">=3.12"
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
target-version = "py312"
[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.12"
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",
]