Compare commits

...

6 Commits

Author SHA1 Message Date
Barcode Betty 7a7d8f451e fix(ci): remove GHA cache-from/cache-to (CAR-1357)
CI / lint (pull_request) Successful in 9s
CI / typecheck (pull_request) Successful in 18s
CI / test (pull_request) Successful in 23s
CI / build-and-push (pull_request) Has been skipped
The build-and-push job fails post-merge of CAR-1356 REGISTRY_TOKEN fix:
cache-from/cache-to: type=gha backend does not exist on Gitea. Build
succeeds but post-build cache export fails and cascades to skipping the
Push Docker image step. Confirmed in uat run 3444 + dev run 3445.

Per CAR-1362, drop cache-from and cache-to from both Build and Push
Docker image steps. Matches proven-green cartsnitch/auth/ci.yml pattern.

Refs: CAR-1362, CAR-1356, CAR-1330, CAR-1357.

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-06-09 18:01:21 +00:00
Barcode Betty 79e8baa609 fix(ci): use REGISTRY_TOKEN for build-and-push registry login (CAR-1330)
CI / lint (push) Successful in 4s
CI / typecheck (push) Successful in 17s
CI / test (push) Successful in 25s
CI / build-and-push (push) Failing after 54s
Squashed fix swaps github.token → secrets.REGISTRY_TOKEN at .gitea/workflows/ci.yml:121, matching the proven-green cartsnitch/auth pattern (CAR-1009). Parity fix with uat PR #49 to prevent reintroduction on next dev→uat promotion.

Note: includes 3 absorbed lint/typecheck commits from PR #48 (already merged to dev via #48) to unblock CI on this branch. No app code changes; one-line CI config swap only.

QA: PR #50 approved by @cs_charlie (review id 4616); CI run 3443 lint/typecheck/test all green.
Co-authored-by: Barcode Betty <32+cs_betty@noreply.git.farh.net>
Co-committed-by: Barcode Betty <32+cs_betty@noreply.git.farh.net>
2026-06-09 17:47:11 +00:00
Savannah Savings 3860a5d061 Merge pull request 'Fix CAR-1132: SQLite UUID binding and User.id defaults in test fixtures' (#42) from betty/car-1132-comprehensive-fix into dev
CI / lint (push) Failing after 7s
CI / typecheck (push) Failing after 17s
CI / lint (pull_request) Failing after 3s
CI / test (push) Successful in 22s
CI / typecheck (pull_request) Failing after 18s
CI / build-and-push (push) Has been skipped
CI / test (pull_request) Successful in 22s
CI / build-and-push (pull_request) Has been skipped
2026-06-09 01:01:09 +00:00
Savannah Savings 7a7aaca064 Fix PostgreSQL connection pool issues (CAR-1077) (#39)
CI / lint (push) Successful in 5s
CI / typecheck (push) Successful in 28s
CI / lint (pull_request) Successful in 6s
CI / test (push) Failing after 1m0s
CI / build-and-push (push) Has been skipped
CI / typecheck (pull_request) Successful in 29s
CI / test (pull_request) Failing after 1m2s
CI / build-and-push (pull_request) Has been skipped
QA approved by Checkout Charlie; CTO Dev review approved by Savannah Savings. Adds pool_timeout=30 and DB-connectivity /health probe. Strict CI improvement (lint+typecheck green); remaining test failure pre-existing on dev, tracked under CAR-1132/PR#42.
2026-06-02 15:10:01 +00:00
Barcode Betty 76781ed238 style: fix ruff format in conftest.py
CI / lint (pull_request) Successful in 5s
CI / typecheck (pull_request) Successful in 29s
CI / test (pull_request) Failing after 1m0s
CI / build-and-push (pull_request) Has been skipped
Add missing blank line between the _set_timestamp_defaults helper
and the next top-level constant so `ruff format --check .` passes.
Pre-existing on dev's HEAD; surfaced after rebasing PR #39 onto dev
in 2b20946.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-02 14:58:18 +00:00
Barcode Betty 2b20946ad7 fix: /health returns 503 on DB failure, pool_timeout=30, CI typecheck fixes
CI / lint (pull_request) Failing after 4s
CI / typecheck (pull_request) Failing after 25s
CI / test (pull_request) Failing after 1m5s
CI / build-and-push (pull_request) Has been skipped
QA review of PR #39 (CAR-1121) identified three blocking issues; this
commit addresses all three plus the typecheck errors flagged as CI RED.

CAR-1077 (PR #39) changes:
- database.py: add pool_timeout=30 so the engine fails fast when the
  connection pool is exhausted (defends against the "server closed
  connection unexpectedly" pod failures).
- routes/health.py: /health now calls SELECT 1 through Depends(get_db)
  and raises HTTPException(503) when the database is unreachable, so
  Kubernetes readiness probes can correctly mark the pod unhealthy and
  stop routing traffic to it.  Logs the failure at exception level for
  observability.
- Drop .mcp.json from this PR (root-level MCP server config, not
  related to the pool fix; tracked separately).

CI typecheck fixes (pre-existing on dev, were failing mypy on PR #39):
- auth/passwords.py: cast bcrypt return values so mypy doesn't widen
  to Any.
- config.py: silence the false-positive call-arg on Settings() — the
  three required fields are populated from the environment by
  pydantic-settings at runtime.
- cache.py: coerce the bytes/str union returned by the redis client
  to the documented str | None return type.
- middleware/rate_limit.py: annotate the three module-level limiters
  with the RateLimitBackend protocol, cast the redis zrange score to
  float before arithmetic, and add max_requests/window_seconds to the
  protocol so the response-header builder can read them.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-02 14:53:16 +00:00
6 changed files with 32 additions and 21 deletions
+1 -4
View File
@@ -118,7 +118,7 @@ jobs:
echo "CalVer tag: $VERSION"
- name: Log in to Gitea Container Registry
run: echo "${{ github.token }}" | docker login git.farh.net -u ${{ github.actor }} --password-stdin
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.farh.net -u ${{ github.actor }} --password-stdin
- name: Extract metadata
id: meta
@@ -140,8 +140,6 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
build-args: |
APT_CACHE_BUST=${{ github.run_id }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Scan api image for vulnerabilities
uses: anchore/scan-action@v5
@@ -168,7 +166,6 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
build-args: |
APT_CACHE_BUST=${{ github.run_id }}
cache-from: type=gha
- name: Create git tag
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
-11
View File
@@ -1,11 +0,0 @@
{
"mcpServers": {
"gitea": {
"type": "http",
"url": "https://git-mcp.farh.net/mcp",
"headers": {
"Authorization": "Bearer ${GITEA_TOKEN}"
}
}
}
}
+2 -2
View File
@@ -4,8 +4,8 @@ import bcrypt
def hash_password(password: str) -> str:
return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
return str(bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode())
def verify_password(plain_password: str, hashed_password: str) -> bool:
return bcrypt.checkpw(plain_password.encode(), hashed_password.encode())
return bool(bcrypt.checkpw(plain_password.encode(), hashed_password.encode()))
+1 -1
View File
@@ -35,7 +35,7 @@ class CacheClient:
async def get(self, key: str) -> str | None:
if not self._client:
return None
value = await self._client.get(key)
value: str | bytes | None = await self._client.get(key)
if value is None:
return None
if isinstance(value, bytes):
+1
View File
@@ -14,6 +14,7 @@ def _build_engine_kwargs() -> dict:
kwargs.update(
pool_size=10,
max_overflow=20,
pool_timeout=30,
pool_pre_ping=True,
pool_recycle=3600,
)
+27 -3
View File
@@ -1,16 +1,40 @@
"""Health check and error metrics endpoints."""
from fastapi import APIRouter, Depends
import logging
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession
from cartsnitch_api.auth.dependencies import verify_service_key
from cartsnitch_api.database import get_db
from cartsnitch_api.middleware.error_handler import get_error_monitor
logger = logging.getLogger(__name__)
router = APIRouter(tags=["health"])
@router.get("/health")
async def health():
return {"status": "ok"}
async def health(db: AsyncSession = Depends(get_db)):
"""Liveness + DB connectivity probe.
Returns HTTP 200 when the API process is responsive *and* the database
is reachable, so Kubernetes readiness probes can correctly route traffic
away from pods that have lost their database connection.
Returns HTTP 503 when the database is unreachable so K8s marks the pod
unhealthy and stops sending traffic to it.
"""
try:
await db.execute(text("SELECT 1"))
except Exception as exc:
logger.exception("Health check failed: database unreachable")
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail={"status": "unavailable", "database": "disconnected"},
) from exc
return {"status": "ok", "database": "connected"}
@router.get("/internal/error-stats", dependencies=[Depends(verify_service_key)])