2025-10-18 21:29:28 -04:00
[ build-system ]
build-backend = "hatchling.build"
requires = [ "hatchling" ]
2025-10-19 21:35:02 -04:00
[ dependency-groups ]
dev = [
2026-06-18 11:19:24 -04:00
"ruff==0.14.1" ,
2026-06-19 07:39:56 -04:00
"pyright==1.1.410" ,
2026-06-18 11:19:24 -04:00
"pydoclint==0.8.3" ,
"pytest==8.4.2" ,
"pytest-asyncio==1.2.0" ,
"pytest-cov==7.0.0" ,
"typeguard==4.4.4" ,
"httpx==0.28.1" , # For testing async HTTP calls
"pytest-mock==3.15.1" ,
2025-10-27 08:39:54 -04:00
# File format and linting tools
2026-06-18 11:19:24 -04:00
"pre-commit==4.3.0" , # For running pre-commit hooks in CI
"pyyaml==6.0.3" ,
"yamllint==1.37.1" ,
"toml-sort==0.24.3" ,
"language-formatters-pre-commit-hooks==2.15.0" , # For pretty-format-toml
"xdoctest==1.3.0" , # For doctest support
"poethepoet==0.41.0" # Task runner for unified development workflows
2025-10-19 21:35:02 -04:00
]
2025-10-27 10:49:05 -04:00
[ project ]
authors = [ { name = "DarkHelm" , email = "darkhelm@darkhelm.org" } ]
classifiers = [
"Development Status :: 3 - Alpha" ,
"Intended Audience :: Developers" ,
"License :: OSI Approved :: MIT License" ,
"Programming Language :: Python :: 3" ,
2026-06-18 11:19:24 -04:00
"Programming Language :: Python :: 3.14"
2025-10-27 10:49:05 -04:00
]
2025-10-30 11:16:45 -04:00
dependencies = [
2026-06-18 11:19:24 -04:00
"fastapi==0.120.2" ,
2026-06-19 07:39:56 -04:00
"sqlalchemy==2.0.44" ,
"psycopg[binary]==3.2.12" ,
2026-06-18 11:19:24 -04:00
"uvicorn==0.38.0"
2025-10-30 11:16:45 -04:00
]
2025-10-27 10:49:05 -04:00
description = "Backend service for Plex playlist management"
keywords = [ "plex" , "playlist" , "media" , "management" ]
license = "MIT"
name = "plex-playlist-backend"
readme = "../README.md"
2026-06-18 11:19:24 -04:00
requires-python = ">=3.14"
2025-10-27 10:49:05 -04:00
version = "0.1.0"
2025-10-19 21:35:02 -04:00
[ tool . coverage ]
[ tool . coverage . report ]
exclude_lines = [
"pragma: no cover" ,
"def __repr__" ,
"raise AssertionError" ,
"raise NotImplementedError" ,
"if __name__ == .__main__.:" ,
"if TYPE_CHECKING:"
2025-10-18 09:14:10 -04:00
]
2025-10-19 21:35:02 -04:00
[ tool . coverage . run ]
omit = [
"*/tests/*" ,
"*/venv/*" ,
"*/.venv/*" ,
"*/node_modules/*" ,
"*/migrations/*"
]
source = [ "src" ]
2025-10-27 10:49:05 -04:00
[ tool . hatch . build ]
include = [
"src/backend/**/*.py" ,
"src/backend/py.typed"
]
2025-10-19 21:35:02 -04:00
[ tool . hatch . build . targets . wheel ]
packages = [ "src/backend" ]
2025-10-27 10:49:05 -04:00
[ tool . hatch . version ]
path = "src/backend/__init__.py"
2026-04-08 09:45:49 -04:00
[ tool . poe ]
2025-10-31 13:25:03 -04:00
[ tool . poe . tasks ]
build-cicd = { shell = "./scripts/build-cicd-local.sh" , help = "Build both CI/CD images" }
# === Docker CI/CD Image Tasks ===
build-cicd-base = { shell = "./scripts/build-cicd-local.sh --base-only" , help = "Build CI/CD base image only" }
build-cicd-complete = { shell = "./scripts/build-cicd-local.sh --complete-only" , help = "Build CI/CD complete image only" }
build-cicd-force = { shell = "./scripts/build-cicd-local.sh --force" , help = "Force rebuild CI/CD images" }
build-if-dockerfile-changed = { shell = "if git diff --quiet HEAD~1 Dockerfile.cicd-base Dockerfile.cicd; then echo 'No Dockerfile changes, skipping build'; else poe build-cicd; fi" , help = "Only build CI/CD images if Dockerfiles changed" }
# === Local CI Simulation ===
ci-format-check = [
{ shell = "cd backend && uv run ruff format --check ." } ,
{ shell = "cd frontend && yarn prettier --check src/" }
]
ci-full = [ "ci-format-check" , "lint" , "type-check" , "test-all" , "docs-backend" ]
ci-quick = [ "ci-format-check" , "lint" , "type-check" ]
# === Utility Tasks ===
clean = [
{ shell = "cd backend && find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true" } ,
{ shell = "cd backend && find . -name '*.pyc' -delete 2>/dev/null || true" } ,
{ shell = "cd frontend && rm -rf node_modules/.cache dist coverage 2>/dev/null || true" } ,
{ shell = "docker system prune -f" }
]
deps-check = [
{ shell = "cd backend && uv pip check" } ,
{ shell = "cd frontend && yarn audit" }
]
# === Dependency Management ===
deps-install = [
{ shell = "cd backend && uv sync --dev" } ,
{ shell = "cd frontend && yarn install" }
]
deps-update = [
{ shell = "cd backend && uv sync --upgrade --dev" } ,
{ shell = "cd frontend && yarn upgrade" }
]
# === Development Environment Tasks ===
dev = { shell = "docker compose -f compose.dev.yml up -d" , help = "Start development environment" }
dev-down = { shell = "docker compose -f compose.dev.yml down" , help = "Stop development environment" }
dev-logs = { shell = "docker compose -f compose.dev.yml logs -f" , help = "Follow development environment logs" }
dev-restart = { shell = "docker compose -f compose.dev.yml restart" , help = "Restart development environment" }
# === Documentation Tasks ===
docs-backend = { shell = "cd backend && uv run xdoctest --module backend" , help = "Run backend docstring tests" }
2026-04-08 09:45:49 -04:00
docs-check = { shell = "cd backend && uv run pydoclint --config=pyproject.toml src/" , help = "Check docstring quality" }
2025-10-31 13:25:03 -04:00
format = [ "format-backend" , "format-frontend" ]
# === Code Formatting Tasks ===
format-backend = { shell = "cd backend && uv run ruff format ." , help = "Format backend Python code" }
format-frontend = { shell = "cd frontend && yarn prettier --write src/" , help = "Format frontend TypeScript code" }
lint = [ "lint-backend" , "lint-frontend" ]
# === Linting Tasks ===
lint-backend = { shell = "cd backend && uv run ruff check ." , help = "Lint backend Python code" }
lint-frontend = { shell = "cd frontend && yarn lint" , help = "Lint frontend TypeScript code" }
# === Parallel Execution for Speed ===
lint-parallel = { shell = "poe lint-backend & poe lint-frontend & wait" , help = "Run linting in parallel for speed" }
# === CI/CD Tasks ===
2026-06-18 11:19:24 -04:00
pre-commit-install = { shell = "pre-commit install --config ../.pre-commit-config.yaml" , help = "Install pre-commit hooks" }
pre-commit-run = { shell = "pre-commit run --all-files --config ../.pre-commit-config.yaml" , help = "Run all pre-commit hooks" }
pre-commit-update = { shell = "pre-commit autoupdate --config ../.pre-commit-config.yaml" , help = "Update pre-commit hook versions" }
2025-10-31 13:25:03 -04:00
# === Quality Gates (Mimics CI Pipeline) ===
quality-gate = [
{ shell = "echo '🔍 Running quality gate checks...'" } ,
"ci-format-check" ,
"lint" ,
"type-check" ,
"test-unit" ,
"docs-check" ,
{ shell = "echo '✅ All quality checks passed!'" }
]
reset = [
"clean" ,
{ shell = "cd backend && rm -rf .venv 2>/dev/null || true" } ,
{ shell = "cd frontend && rm -rf node_modules 2>/dev/null || true" } ,
"deps-install"
]
2026-07-07 08:14:10 -04:00
runner-diagnostics = { shell = "xonsh ./scripts/gitea-actions/collect_runner_diagnostics.xsh" , help = "Collect recent runner, docker, and system diagnostics from all known runner hosts" }
runner-discover = { shell = "xonsh ./scripts/gitea-actions/discover-runners.xsh --batch" , help = "Collect runner inventory, labels, and baseline health from all known runner hosts" }
2025-10-31 13:25:03 -04:00
# === Development Setup (New Developer Onboarding) ===
setup = [
"deps-install" ,
"pre-commit-install" ,
"dev" ,
{ shell = "echo '✓ Development environment ready!'" } ,
{ shell = "echo ' - Frontend: http://localhost:3000'" } ,
{ shell = "echo ' - Backend API: http://localhost:8000'" } ,
{ shell = "echo ' - API Docs: http://localhost:8000/docs'" }
]
test-all = [ "test-backend" , "test-frontend" , "test-integration" ]
# === Testing Tasks ===
2026-07-06 16:51:39 -04:00
test-backend = { shell = "cd backend && uv run pytest -m 'not integration'" , help = "Run backend unit tests (excludes integration marker)" }
test-backend-cov = { shell = "cd backend && uv run pytest -m 'not integration' --cov" , help = "Run backend unit tests with coverage (excludes integration marker)" }
test-e2e = { shell = "bash ./scripts/run-ci-e2e-compose.sh" , help = "Run E2E tests via compose against deployable runtime images" }
2025-10-31 13:25:03 -04:00
test-frontend = { shell = "cd frontend && yarn test" , help = "Run frontend unit tests" }
test-frontend-cov = { shell = "cd frontend && yarn test:coverage" , help = "Run frontend tests with coverage" }
test-full = [ "test-backend-cov" , "test-frontend-cov" , "test-integration" , "test-e2e" ]
# === Smart Conditional Tasks ===
test-if-changed = { shell = "if git diff --quiet HEAD~1 backend/ frontend/; then echo 'No changes detected, skipping tests'; else poe test-unit; fi" , help = "Only run tests if code has changed" }
2026-07-06 16:51:39 -04:00
test-integration = { shell = "bash ./scripts/run-ci-integration-compose.sh" , help = "Run integration tests via compose against deployable runtime image" }
2025-10-31 13:25:03 -04:00
test-parallel = { shell = "poe test-backend & poe test-frontend & wait" , help = "Run unit tests in parallel for speed" }
# === Comprehensive Testing ===
test-unit = [ "test-backend" , "test-frontend" ]
type-check = [ "type-check-backend" , "type-check-frontend" ]
# === Type Checking Tasks ===
type-check-backend = { shell = "cd backend && uv run pyright ." , help = "Type check backend Python code" }
type-check-frontend = { shell = "cd frontend && yarn type-check" , help = "Type check frontend TypeScript code" }
type-check-parallel = { shell = "poe type-check-backend & poe type-check-frontend & wait" , help = "Run type checking in parallel for speed" }
2026-04-08 09:45:49 -04:00
# Pydoclint configuration for backend
[ tool . pydoclint ]
arg-type-hints-in-docstring = false
arg-type-hints-in-signature = true
check-return-types = false
check-yield-types = false
style = "google"
2025-10-19 21:35:02 -04:00
[ tool . pytest . ini_options ]
addopts = [
"--strict-markers" ,
"--strict-config" ,
"--verbose" ,
"--cov=backend" ,
"--cov-report=term-missing:skip-covered" ,
"--cov-report=html" ,
2025-10-23 12:58:32 -04:00
"--cov-report=xml"
2025-10-19 21:35:02 -04:00
]
markers = [
"slow: marks tests as slow (deselect with '-m \"not slow\"')" ,
"integration: marks tests as integration tests"
]
python_classes = [ "Test*" ]
python_files = [ "test_*.py" , "*_test.py" ]
python_functions = [ "test_*" ]
testpaths = [ "tests" ]
[ tool . ruff ]
2025-10-18 09:14:10 -04:00
line-length = 88
2025-10-19 21:35:02 -04:00
src = [ "src" ]
2026-06-18 11:19:24 -04:00
target-version = "py314"
2025-10-18 09:14:10 -04:00
[ tool . ruff . lint ]
ignore = [
2025-10-19 21:35:02 -04:00
"E501" , # line too long, handled by black
2025-10-18 09:14:10 -04:00
"B008" , # do not perform function calls in argument defaults
2025-10-19 21:35:02 -04:00
"B903" # Use `collections.abc.MutableMapping` instead of `typing.MutableMapping`
2025-10-18 09:14:10 -04:00
]
select = [
"E" , # pycodestyle errors
"W" , # pycodestyle warnings
"F" , # pyflakes
"I" , # isort
"B" , # flake8-bugbear
"C4" , # flake8-comprehensions
"UP" , # pyupgrade
"ARG" , # flake8-unused-arguments
"SIM" , # flake8-simplify
2025-10-19 21:35:02 -04:00
"TCH" # flake8-type-checking
2025-10-18 09:14:10 -04:00
]
2025-10-30 08:09:45 -04:00
[ tool . ruff . lint . isort ]
known-first-party = [ "backend" ]
section-order = [ "future" , "standard-library" , "third-party" , "first-party" , "local-folder" ]
2025-10-18 09:14:10 -04:00
[ tool . ruff . lint . per-file-ignores ]
2025-10-19 21:35:02 -04:00
"tests/*" = [ "ARG" , "S101" ]