Skip to content

Commit 14827d5

Browse files
chore(toolchain): refresh lockfile, declare db deps, and align hooks with uv.lock
The lockfile had not moved since March: google-adk sat at 1.26.0 while PyPI shipped 2.8.0, and `uv-secure` was failing the CI lint job on stale starlette, urllib3, and sqlparse advisories (this was the red `lint` check on #156). Upgrading surfaced a packaging defect: google-adk 2.x moved SQLAlchemy and aiosqlite behind its optional `db` extra, so this package imported `sqlalchemy` without declaring it and would fail at import for anyone installing against ADK 2.x. `aiosqlite` stayed a core ADK dependency but is declared too, since it is the driver behind our default URL. The pre-commit line also pinned ruff, uv, and uv-secure through mirrors whose versions drifted from the lockfile CI installs, the same pattern quantfit and saucier already moved away from. - Declare `sqlalchemy[asyncio]>=2.0` and `aiosqlite>=0.21` as direct dependencies; `uv lock --upgrade` to google-adk 2.8.0, cryptography 50.0.1, ruff 0.16.5, ty 0.0.78, pytest 9.1.1, docvet 1.15.1 - Run every Python hook through `uv run` so pre-commit and CI share one pinned version; add `pre-commit-hooks` whitespace/EOF/merge-marker/large-file checks; pin the docvet and actionlint actions by SHA in CI and add yamllint + actionlint steps to the lint job - Adopt ruff 0.16 defaults: keep `E402` on, ignore `RUF022` (our `__all__` ordering is plain-alphabetical, enforced by `test_public_api.py`), allow `DTZ` in tests and spike scripts, let ruff format Markdown code fences; exclude the framework-generated `_bmad`, `_bmad-output`, `.specify`, and `.claude/commands` trees from ruff and from the whitespace hooks - Move to PEP 639 license metadata (silences the uv_build classifier warning), widen `uv_build` to `<0.13`, drop the now-unused pygments `uv-secure` ignore, refresh the hook tables in CONTRIBUTING and the development guide
1 parent cd0a884 commit 14827d5

24 files changed

Lines changed: 2065 additions & 2569 deletions

.claude/rules/pytest.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,12 @@ tests/
6767
```python
6868
import pytest
6969

70+
7071
@pytest.fixture
7172
def fernet_backend():
7273
return FernetBackend(key=Fernet.generate_key())
7374

75+
7476
@pytest.fixture
7577
def database_session():
7678
conn = create_connection()
@@ -97,6 +99,7 @@ def make_backend():
9799
if key is None:
98100
key = Fernet.generate_key()
99101
return FernetBackend(key=key)
102+
100103
return _make_backend
101104
```
102105

@@ -109,11 +112,15 @@ def make_backend():
109112
## Parametrization
110113

111114
```python
112-
@pytest.mark.parametrize("input_data, expected", [
113-
(b"hello", b"hello"),
114-
(b"", b""),
115-
(b"\x00\xff", b"\x00\xff"),
116-
], ids=["ascii", "empty", "binary"])
115+
@pytest.mark.parametrize(
116+
"input_data, expected",
117+
[
118+
(b"hello", b"hello"),
119+
(b"", b""),
120+
(b"\x00\xff", b"\x00\xff"),
121+
],
122+
ids=["ascii", "empty", "binary"],
123+
)
117124
async def test_encrypt_decrypt_roundtrip(backend, input_data, expected):
118125
encrypted = await backend.encrypt(input_data)
119126
decrypted = await backend.decrypt(encrypted)
@@ -148,6 +155,7 @@ Use `pytest-asyncio` for async test functions:
148155
```python
149156
import pytest
150157

158+
151159
@pytest.mark.asyncio
152160
async def test_encrypt_returns_bytes(backend):
153161
result = await backend.encrypt(b"plaintext")

.claude/rules/python.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ Do not annotate `self` or `cls` parameters.
129129

130130
Use f-strings for string interpolation. For logging, use %-formatting with lazy evaluation:
131131
```python
132-
logger.info('Processing session: %s', session_id)
132+
logger.info("Processing session: %s", session_id)
133133
```
134134

135135
## Boolean and None Checks

.github/workflows/ci.yml

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,26 @@ jobs:
2727
- run: uv sync --locked --dev
2828
- run: uv run ruff check .
2929
- run: uv run ruff format --check .
30-
- run: uv run uv-secure
30+
# Retry once on exit 3 only — uv-secure's advisory-fetch crash class
31+
# ("<package> raised exception"), which quantfit also hits. Real
32+
# findings exit 1 and still fail immediately.
33+
- name: uv-secure
34+
run: |
35+
code=0
36+
uv run uv-secure || code=$?
37+
if [ "$code" -eq 3 ]; then
38+
echo "uv-secure crashed (exit 3) — one retry"
39+
uv run uv-secure
40+
else
41+
exit "$code"
42+
fi
3143
- run: uv run lint-imports
44+
- run: uv run yamllint -c .yamllint.yaml .
45+
# Pin the same version .pre-commit-config.yaml names. The action
46+
# defaults to `latest`, which drifts from the hook line.
47+
- uses: raven-actions/actionlint@3d39aea434753780c3b3d4a1a31c854b4dbf49d7 # v2
48+
with:
49+
version: 1.7.11
3250
- run: uv cache prune --ci
3351
if: always()
3452
continue-on-error: true
@@ -90,4 +108,4 @@ jobs:
90108
- uses: actions/checkout@v6
91109
with:
92110
fetch-depth: 0
93-
- uses: Alberto-Codes/docvet@v1
111+
- uses: Alberto-Codes/docvet@257310e5edc8249629d388d879a5afde602f02ca # v1

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,9 @@ cython_debug/
211211
.abstra/
212212

213213
# Visual Studio Code
214-
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
214+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
215215
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
216-
# and can be added to the global gitignore or merged into this file. However, if you prefer,
216+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
217217
# you could uncomment the following to ignore the entire vscode folder
218218
# .vscode/
219219

.pre-commit-config.yaml

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,92 @@
1+
# Every Python tool here runs from the project environment through
2+
# `uv run`, so the lockfile pins one version for both this hook line and
3+
# CI. A pre-commit mirror carries its own pin that drifts from the
4+
# lockfile CI installs, and the two gates then disagree.
5+
#
6+
# actionlint is a Go binary with no lockfile entry; the CI step pins the
7+
# same 1.7.11 this mirror does.
8+
default_stages: [pre-commit]
9+
110
repos:
2-
- repo: https://github.com/adrienverge/yamllint
3-
rev: v1.38.0
11+
- repo: https://github.com/pre-commit/pre-commit-hooks
12+
rev: v6.0.0
413
hooks:
5-
- id: yamllint
6-
args: [-c, .yamllint.yaml]
14+
- id: end-of-file-fixer
15+
exclude: ^(_bmad|_bmad-output|\.specify|\.claude/commands)/
16+
- id: trailing-whitespace
17+
exclude: ^(_bmad|_bmad-output|\.specify|\.claude/commands)/
18+
- id: check-merge-conflict
19+
- id: check-added-large-files
20+
args: [--maxkb=1024]
721

822
- repo: https://github.com/rhysd/actionlint
923
rev: v1.7.11
1024
hooks:
1125
- id: actionlint
1226

13-
- repo: https://github.com/astral-sh/ruff-pre-commit
14-
rev: v0.15.4
27+
- repo: local
1528
hooks:
29+
- id: yamllint
30+
name: yamllint
31+
entry: uv run yamllint -c .yamllint.yaml .
32+
language: system
33+
pass_filenames: false
34+
always_run: true
35+
1636
- id: ruff-check
37+
name: ruff check
38+
entry: uv run ruff check .
39+
language: system
40+
pass_filenames: false
41+
always_run: true
42+
1743
- id: ruff-format
44+
name: ruff format
45+
entry: uv run ruff format --check .
46+
language: system
47+
pass_filenames: false
48+
always_run: true
1849

19-
- repo: https://github.com/astral-sh/uv-pre-commit
20-
rev: 0.10.7
21-
hooks:
50+
# `uv lock --check` reports a stale lockfile where the mirror's bare
51+
# `uv lock` rewrote it, matching CI.
2252
- id: uv-lock
53+
name: uv lock is current
54+
entry: uv lock --check
55+
language: system
56+
pass_filenames: false
57+
files: ^(uv\.lock|pyproject\.toml|uv\.toml)$
2358

24-
- repo: https://github.com/owenlamont/uv-secure
25-
rev: 0.17.0
26-
hooks:
2759
- id: uv-secure
60+
name: uv-secure
61+
entry: uv run uv-secure
62+
language: system
63+
pass_filenames: false
64+
files: ^uv\.lock$
2865

29-
- repo: local
30-
hooks:
3166
- id: lint-imports
3267
name: import-linter
3368
entry: uv run lint-imports
3469
language: system
35-
types: [python]
3670
pass_filenames: false
71+
always_run: true
3772

3873
- id: ty-check
3974
name: ty type check
4075
entry: uv run ty check src/
4176
language: system
42-
types: [python]
4377
pass_filenames: false
78+
always_run: true
4479

4580
- id: pytest
4681
name: pytest
4782
entry: uv run pytest --tb=short -q
4883
language: system
49-
types: [python]
5084
pass_filenames: false
85+
always_run: true
5186

5287
- id: docvet
5388
name: docvet check
5489
entry: uv run docvet check --staged
5590
language: system
56-
types: [python]
5791
pass_filenames: false
92+
always_run: true

CONTRIBUTING.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,18 @@ This project uses pre-commit hooks to catch issues before they reach CI. Pre-com
8181
pre-commit install
8282
```
8383

84-
Seven hooks run automatically on each commit:
84+
Every Python tool runs through `uv run`, so the hook line and CI share the version pinned in `uv.lock`. These hooks run automatically on each commit (`uv-lock` and `uv-secure` only when `pyproject.toml` or `uv.lock` is staged):
8585

8686
| Hook | What it checks |
8787
|------|---------------|
88-
| yamllint | YAML syntax and formatting |
88+
| pre-commit-hooks | Trailing whitespace, final newline, merge markers, files over 1 MB |
8989
| actionlint | GitHub Actions workflow validity |
90+
| yamllint | YAML syntax and formatting |
9091
| ruff-check | Python linting |
91-
| ruff-format | Python code formatting |
92+
| ruff-format | Python and Markdown code-fence formatting |
93+
| uv-lock | `uv.lock` is current with `pyproject.toml` |
94+
| uv-secure | Known vulnerabilities in locked dependencies |
95+
| import-linter | Layered-architecture contracts hold |
9296
| ty | Type checking (`src/` only) |
9397
| pytest | Full test suite |
9498
| docvet | Docstring quality on staged files |
@@ -233,11 +237,13 @@ Use appropriate markers for your tests:
233237
```python
234238
import pytest
235239

240+
236241
@pytest.mark.unit
237242
async def test_fernet_encrypt_decrypt():
238243
"""Unit test for Fernet round-trip encryption."""
239244
...
240245

246+
241247
@pytest.mark.integration
242248
async def test_encrypted_session_persistence():
243249
"""Integration test with real SQLite database."""

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ uv add adk-secure-sessions
3131
```python
3232
# Before (ADK default — unencrypted):
3333
from google.adk.sessions import DatabaseSessionService
34+
3435
session_service = DatabaseSessionService(db_url="sqlite+aiosqlite:///sessions.db")
3536

3637
# After (encrypted — swap the import and constructor):
3738
from adk_secure_sessions import EncryptedSessionService, FernetBackend
39+
3840
session_service = EncryptedSessionService(
3941
db_url="sqlite+aiosqlite:///sessions.db", backend=FernetBackend("your-secret-key")
4042
)

docs/adr/ADR-001-protocol-based-interfaces.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Use **`typing.Protocol`** (PEP 544) for all public interfaces.
2727
```python
2828
from typing import Protocol, runtime_checkable
2929

30+
3031
@runtime_checkable
3132
class EncryptionBackend(Protocol):
3233
"""Contract for all encryption backends."""

docs/development-guide.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,18 @@ uv run ruff check . # Lint check
4949

5050
## Pre-Commit Hooks
5151

52-
Seven hooks run on each commit:
52+
Every Python tool runs through `uv run`, so the hook line and CI share the version pinned in `uv.lock`. Only `actionlint` (a Go binary) and the generic `pre-commit-hooks` checks come from pre-commit mirrors.
5353

5454
| Hook | What it checks |
5555
|------|---------------|
56-
| yamllint | YAML syntax and formatting |
56+
| pre-commit-hooks | Trailing whitespace, final newline, merge markers, files over 1 MB |
5757
| actionlint | GitHub Actions workflow validity |
58+
| yamllint | YAML syntax and formatting |
5859
| ruff-check | Python linting |
59-
| ruff-format | Python code formatting |
60+
| ruff-format | Python and Markdown code-fence formatting |
61+
| uv-lock | `uv.lock` is current with `pyproject.toml` |
62+
| uv-secure | Known vulnerabilities in locked dependencies |
63+
| import-linter | Layered-architecture contracts hold |
6064
| ty | Type checking (`src/` only) |
6165
| pytest | Full test suite |
6266
| docvet | Docstring quality on staged files |
@@ -113,4 +117,3 @@ Install with: `pre-commit install`
113117
- **Markers**: `@pytest.mark.unit`, `@pytest.mark.integration`
114118
- **Fixtures**: Async generators with proper teardown (`yield svc; await svc.close()`)
115119
- **Warnings**: Treated as errors; third-party warnings filtered in `pyproject.toml`
116-

docs/getting-started.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@ just two changes:
4141
```python
4242
# Before (ADK default — unencrypted):
4343
from google.adk.sessions import DatabaseSessionService
44+
4445
session_service = DatabaseSessionService(db_url="sqlite+aiosqlite:///sessions.db")
4546

4647
# After (encrypted — swap the import and constructor):
4748
from adk_secure_sessions import EncryptedSessionService, FernetBackend
49+
4850
session_service = EncryptedSessionService(
4951
db_url="sqlite+aiosqlite:///sessions.db",
5052
backend=FernetBackend("your-secret-passphrase"),
@@ -150,7 +152,7 @@ import sqlite3
150152
conn = sqlite3.connect("sessions.db")
151153
row = conn.execute("SELECT state FROM sessions LIMIT 1").fetchone()
152154
print(type(row[0])) # <class 'str'>
153-
print(row[0][:40]) # First 40 chars of base64-encoded envelope
155+
print(row[0][:40]) # First 40 chars of base64-encoded envelope
154156
conn.close()
155157
```
156158

0 commit comments

Comments
 (0)