From d9b824851a0b6daac7154c08faa179d032c30c44 Mon Sep 17 00:00:00 2001 From: Malcolm Roberts Date: Fri, 2 Oct 2026 15:06:56 -0500 Subject: [PATCH] chore: add mise format and lint tasks (ruff + Biome) Tools are pinned in mise.toml. ruff.toml points first-party detection at backend/, since the frontend's src/auth, src/books and src/notes otherwise get matched as first-party. biome.json uses spaces to match the existing Prettier-style formatting. Also combines a nested with in test_passwords.py (ruff SIM117). --- backend/tests/test_passwords.py | 9 +++++---- biome.json | 6 ++++++ mise.toml | 27 ++++++++++++++++++++++++++- ruff.toml | 4 ++++ 4 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 biome.json create mode 100644 ruff.toml diff --git a/backend/tests/test_passwords.py b/backend/tests/test_passwords.py index a406675..2dda93c 100644 --- a/backend/tests/test_passwords.py +++ b/backend/tests/test_passwords.py @@ -8,7 +8,6 @@ from unittest import mock from auth import ITERATIONS, hash_password, load_pepper, needs_rehash, verify_password PEPPER = b"p" * 32 -# Independently computed: HMAC-SHA256(PEPPER, password) -> PBKDF2-SHA256, 1000 iterations, 16 zero-byte salt. KNOWN_ANSWER = "pbkdf2_sha256$1000$AAAAAAAAAAAAAAAAAAAAAA==$9ZLLEusnEPU3Km8h+vnd4ue9fw7rHdm4QwuBX5ozynE=" @@ -41,9 +40,11 @@ class PasswordHashTests(unittest.TestCase): class PepperTests(unittest.TestCase): def test_env_pepper_must_be_long_enough(self): - with mock.patch.dict(os.environ, {"PASSWORD_PEPPER": "short"}): - with self.assertRaisesRegex(RuntimeError, "at least 32 characters"): - load_pepper(Path("/nonexistent")) + with ( + mock.patch.dict(os.environ, {"PASSWORD_PEPPER": "short"}), + self.assertRaisesRegex(RuntimeError, "at least 32 characters"), + ): + load_pepper(Path("/nonexistent")) def test_env_pepper_is_used_when_set(self): with mock.patch.dict(os.environ, {"PASSWORD_PEPPER": "e" * 40}): diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..ad5c11b --- /dev/null +++ b/biome.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.5.15/schema.json", + "formatter": { + "indentStyle": "space" + } +} diff --git a/mise.toml b/mise.toml index a228238..b7d9f00 100644 --- a/mise.toml +++ b/mise.toml @@ -1,10 +1,11 @@ [tools] python = "3.14" node = "24" +ruff = "0.16.10" +biome = "2.5.15" [tasks.setup] description = "Create the venv and install backend and frontend dependencies" -# ponytail: reinstalls on every run (fast when up to date); add sources/outputs if it ever gets slow run = [ "python -m venv .venv", ".venv/bin/pip install -q --disable-pip-version-check -r backend/requirements.txt", @@ -31,3 +32,27 @@ run = "npm run dev" [tasks.dev] description = "Start the app for local testing (API + UI)" depends = ["dev:api", "dev:web"] + +[tasks."format:py"] +description = "Sort imports and format the backend" +run = ["ruff check --select I --fix backend", "ruff format backend"] + +[tasks."format:ts"] +description = "Sort imports and format the frontend" +run = "biome check --write --linter-enabled=false src vite.config.ts" + +[tasks.format] +description = "Format and auto-fix Python and TypeScript" +depends = ["format:py", "format:ts"] + +[tasks."lint:py"] +description = "Lint the backend and verify formatting (no writes)" +run = ["ruff check backend", "ruff format --check backend"] + +[tasks."lint:ts"] +description = "Lint the frontend and verify formatting (no writes)" +run = "biome check src vite.config.ts" + +[tasks.lint] +description = "Lint and format-check Python and TypeScript (CI-safe, no writes)" +depends = ["lint:py", "lint:ts"] diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..b0f8550 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,4 @@ +# The backend imports its modules as top-level names (`import db`). Without this, ruff's +# first-party detection matches the frontend's src/auth, src/books, src/notes directories +# instead, and splits backend imports into two groups. +src = ["backend"]