From 8265c62fa9bed4f04a4d041a97e4937c150bea3a Mon Sep 17 00:00:00 2001 From: febux Date: Wed, 12 Aug 2026 21:41:31 +0300 Subject: [PATCH] Pre-commit and Github workflows were added. --- docs/DEPLOY.md | 4 +- migrations/versions/0001_initial.py | 9 +-- migrations/versions/0002_api_keys.py | 1 + pyproject.toml | 4 ++ uv.lock | 99 ++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 6 deletions(-) diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md index 5e7b7a9..7cf873b 100644 --- a/docs/DEPLOY.md +++ b/docs/DEPLOY.md @@ -155,8 +155,8 @@ docker compose --profile services up -d --build ### 4.2 depends_on и healthchecks -`api` ждёт `postgres`, `rabbitmq`, `minio-init` (healthy / completed). -`bot` ждёт только `api` (healthy) — не инфра напрямую, соблюдая hexagonal boundary. +`api` ждёт `postgres`, `rabbitmq`, `minio-init` (healthy / completed). +`bot` ждёт только `api` (healthy) — не инфра напрямую, соблюдая hexagonal boundary. Workers ждут `postgres`, `rabbitmq`, `minio-init`. --- diff --git a/migrations/versions/0001_initial.py b/migrations/versions/0001_initial.py index f9df8ea..e72b213 100644 --- a/migrations/versions/0001_initial.py +++ b/migrations/versions/0001_initial.py @@ -8,6 +8,7 @@ Hand-written (docs/ARCHITECTURE.md §7). status/queue/adapter columns are TEXT+C (additive migrations). UUIDs default to gen_random_uuid() server-side (built into Postgres 13+; we run pg16). """ + from __future__ import annotations from collections.abc import Sequence @@ -84,7 +85,9 @@ def upgrade() -> None: name="documents_status_check", ), ) - op.create_index("documents_user_created_idx", "documents", ["user_id", sa.text("created_at DESC")]) + op.create_index( + "documents_user_created_idx", "documents", ["user_id", sa.text("created_at DESC")] + ) op.create_index("documents_status_idx", "documents", ["status"]) # ── reports ────────────────────────────────────────────────────────────── @@ -181,9 +184,7 @@ def upgrade() -> None: server_default=sa.func.now(), ), sa.Column("last_used_at", sa.DateTime(timezone=True)), - sa.CheckConstraint( - "adapter IN ('bot','web','cli')", name="service_tokens_adapter_check" - ), + sa.CheckConstraint("adapter IN ('bot','web','cli')", name="service_tokens_adapter_check"), ) # ── invoices (STUB — no ЮKassa logic this refactor) ───────────────────── diff --git a/migrations/versions/0002_api_keys.py b/migrations/versions/0002_api_keys.py index f0035aa..d5afc1e 100644 --- a/migrations/versions/0002_api_keys.py +++ b/migrations/versions/0002_api_keys.py @@ -7,6 +7,7 @@ Create Date: 2026-08-07 Hand-written (docs/ARCHITECTURE.md §7 + docs/TICKETS.md T-E3-001). status/rate columns are TEXT+CHECK / INTEGER so later migrations remain additive. """ + from __future__ import annotations from collections.abc import Sequence diff --git a/pyproject.toml b/pyproject.toml index e09559d..a62cbc4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -112,6 +112,7 @@ dev = [ "aiosqlite>=0.20", "testcontainers[rabbitmq,postgres,minio]>=4", "asgi-lifespan>=2.1.0", + "pre-commit>=4.6.2", ] [tool.ruff] @@ -147,6 +148,9 @@ ignore_missing_imports = true testpaths = ["tests"] addopts = "-q" asyncio_mode = "auto" +# Make `tests` importable as a namespace package so integration modules can use +# `from tests.integration.conftest import ...` without committing __init__.py. +pythonpath = ["."] markers = [ "integration: slow tests needing real Postgres/RabbitMQ/MinIO (deselect with '-m \"not integration\"')", ] diff --git a/uv.lock b/uv.lock index 92103e9..bf021bb 100644 --- a/uv.lock +++ b/uv.lock @@ -465,6 +465,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" }, ] +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + [[package]] name = "charset-normalizer" version = "3.4.9" @@ -603,6 +612,7 @@ dev = [ { name = "opentelemetry-instrumentation-httpx" }, { name = "opentelemetry-sdk" }, { name = "pillow" }, + { name = "pre-commit" }, { name = "prometheus-client" }, { name = "pyjwt", extra = ["crypto"] }, { name = "pymupdf" }, @@ -714,6 +724,7 @@ dev = [ { name = "opentelemetry-instrumentation-httpx", specifier = ">=0.45b0" }, { name = "opentelemetry-sdk", specifier = ">=1.24" }, { name = "pillow", specifier = ">=10" }, + { name = "pre-commit", specifier = ">=4.6.2" }, { name = "prometheus-client", specifier = ">=0.20" }, { name = "pyjwt", extras = ["crypto"], specifier = ">=2.8" }, { name = "pymupdf", specifier = ">=1.24" }, @@ -807,6 +818,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/57/30/4a22984d4f1bdfb8c054f07a92bc176b97a3134cc1d6c4b3bffb1f3688b4/cryptography-50.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:d24fead1d4d076e1bfb006dcec392074a3cd8d7b4fc8a595aa64073b2b7a96ba", size = 3874135, upload-time = "2026-07-31T14:24:50.085Z" }, ] +[[package]] +name = "distlib" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/02/bd72be9134d25ed783ecbbc38a539ffaefbf90c78418c7fb7229600dbac7/distlib-0.4.3.tar.gz", hash = "sha256:f152097224a0ae24be5a0f6bae1b9359af82133bce63f98a95f86cae1aede9ed", size = 615141, upload-time = "2026-06-12T08:04:52.847Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/08/9c41fb51ab5b43eb21674aff13df270e8ba6c4b29c8624e328dc7a9482af/distlib-0.4.3-py2.py3-none-any.whl", hash = "sha256:4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b", size = 470628, upload-time = "2026-06-12T08:04:50.506Z" }, +] + [[package]] name = "docker" version = "7.2.0" @@ -837,6 +857,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/03/10388a42375ee7e4ac9b94eb2c5c569c8b5795e377e701c9ac3ad63de890/fastapi-0.141.1-py3-none-any.whl", hash = "sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3", size = 131954, upload-time = "2026-07-29T17:18:04.364Z" }, ] +[[package]] +name = "filelock" +version = "3.32.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/57/3ba6e6cb097f85b855b00163d169f35365f44277df044dcf96d55b8f62a3/filelock-3.32.2.tar.gz", hash = "sha256:c33351e1f49cae33414acbc6d56784e6ecee82514ec90795da1161fc4836b5b8", size = 217172, upload-time = "2026-07-29T22:46:04.895Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/e8/72f8cef9fdfeffe06213fe8508039396ee48daa0e3259457ed766173bfd6/filelock-3.32.2-py3-none-any.whl", hash = "sha256:87dd94cf281e586d135fa51132b8e3d9a598b316e90377a288663c9321036c82", size = 98830, upload-time = "2026-07-29T22:46:03.52Z" }, +] + [[package]] name = "frozenlist" version = "1.8.0" @@ -1102,6 +1131,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] +[[package]] +name = "identify" +version = "2.6.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567, upload-time = "2026-04-17T18:39:50.265Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397, upload-time = "2026-04-17T18:39:49.221Z" }, +] + [[package]] name = "idna" version = "3.18" @@ -1485,6 +1523,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + [[package]] name = "opentelemetry-api" version = "1.44.0" @@ -1767,6 +1814,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, ] +[[package]] +name = "platformdirs" +version = "4.11.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/98/0bf930c4f97d0266b58a89e36c015f56232c52b5d2f207215d48cca9e8f7/platformdirs-4.11.2.tar.gz", hash = "sha256:3a2ae5fca3520a01ab1be8b45613537f52ddf5b5f6f53d88233892dfbf0cd82d", size = 32716, upload-time = "2026-08-10T15:48:06.092Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/e2/4e6eee633809c376c024821b91ade709cbfd040ec53939ffbcc292aa7eee/platformdirs-4.11.2-py3-none-any.whl", hash = "sha256:7f89089b6ea71bda7962953edcf784b2e2d9d285b40ad88be2bb75c6e9d82ab4", size = 23361, upload-time = "2026-08-10T15:48:04.855Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -1776,6 +1832,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] +[[package]] +name = "pre-commit" +version = "4.6.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/89/1f3e8e1fc3e97de0fa963495832f581f025f29471602a309e48808244292/pre_commit-4.6.2.tar.gz", hash = "sha256:8f5d7bfb021ecdbcd9d49d89847082dd24172ccde534390081a679ad046e2441", size = 198670, upload-time = "2026-08-10T22:07:18.421Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/e2/bbb7129c9e7999a6b8ee9cca3b66486c25c423ab5a75f34071798b74ce94/pre_commit-4.6.2-py2.py3-none-any.whl", hash = "sha256:e2dde9a75d3bce11bd3831c26d134df00a2803c1d818be6a0383c3dcda25dc4e", size = 226202, upload-time = "2026-08-10T22:07:16.942Z" }, +] + [[package]] name = "prometheus-client" version = "0.26.0" @@ -2083,6 +2155,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" }, ] +[[package]] +name = "python-discovery" +version = "1.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/b7/ac44da2cf0e53ada0e419033c2d058219c95dc1403126f163304c9e814b1/python_discovery-1.5.2.tar.gz", hash = "sha256:45fd4f20a4e3f9b7bf2e0817870bc8e3b320a19658da177af800768c82dbf354", size = 82350, upload-time = "2026-08-12T14:05:26.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/45/689603d04b3bb8d7faa00f25c24acef993aab7813b3dbbfc472a459ab0b5/python_discovery-1.5.2-py3-none-any.whl", hash = "sha256:3e338c2d0f15dfaeea57493f4c2c6caebe0e998ea815c30ae8bf8ee21f1112d3", size = 38350, upload-time = "2026-08-12T14:05:25.113Z" }, +] + [[package]] name = "python-docx" version = "1.2.0" @@ -2407,6 +2491,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, ] +[[package]] +name = "virtualenv" +version = "21.7.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "python-discovery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/dc/a6eb1ddfa7f1e390fa599b078453c97edb3f6f846b34fb4eac3e8ea16401/virtualenv-21.7.4.tar.gz", hash = "sha256:c9d960c95fa458171e58222a5ccab7465298e4b6559977865e627c4719f1e825", size = 5345511, upload-time = "2026-08-10T22:54:33.316Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/4c/eb2f52aeeaf30dbd073d315a251a63ae2b8263171ec4428c135140cb0802/virtualenv-21.7.4-py3-none-any.whl", hash = "sha256:376ec93cd6aab3044fa395d7db226db38043b7b5748948044b2a87168525e843", size = 5324444, upload-time = "2026-08-10T22:54:31.515Z" }, +] + [[package]] name = "watchfiles" version = "1.2.0"