Skip to content

Commit 3ec0f4d

Browse files
authored
Merge branch 'master' into dependabot/pip/fastapi-bab5c534e0
2 parents 1c2b0db + b216cad commit 3ec0f4d

File tree

3 files changed

+254
-480
lines changed

3 files changed

+254
-480
lines changed

.github/copilot-instructions.md

Lines changed: 154 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,178 @@
1-
# GitHub Copilot Instructions
1+
# Copilot Instructions
22

3-
> **⚡ Token Efficiency Note**: This is a minimal pointer file (~500 tokens, auto-loaded by Copilot).
4-
> For complete operational details, reference: `#file:AGENTS.md` (~2,500 tokens, loaded on-demand)
5-
> For specialized knowledge, use: `#file:SKILLS/<skill-name>/SKILL.md` (loaded on-demand when needed)
3+
## Project Summary
64

7-
## 🎯 Quick Context
5+
RESTful API with Python 3.13 + FastAPI demonstrating modern async patterns. Player registry with CRUD operations, SQLite + SQLAlchemy 2.0 (async), Pydantic validation, containerization. Part of multi-language comparison study (Java, .NET, TypeScript, Python, Go, Rust). Target: 80%+ test coverage.
86

9-
**Project**: FastAPI REST API demonstrating modern Python async patterns
10-
**Stack**: Python 3.13 • FastAPI • SQLAlchemy (async) • SQLite • Docker • pytest
11-
**Pattern**: Routes → Services → Database (layered architecture)
12-
**Philosophy**: Learning-focused PoC emphasizing async/await and type safety
7+
## Quick Start
138

14-
## 📐 Core Conventions
9+
```bash
10+
# Install dependencies
11+
pip install -r requirements.txt
12+
pip install -r requirements-lint.txt
13+
pip install -r requirements-test.txt
14+
15+
# Run development server
16+
uvicorn main:app --reload --port 9000
17+
# Access: http://localhost:9000/docs
18+
19+
# Run tests with coverage
20+
pytest --cov=./ --cov-report=html
21+
22+
# Lint and format
23+
flake8 .
24+
black --check . # or: black . (to auto-format)
25+
26+
# Docker
27+
docker compose up
28+
docker compose down -v # Reset database
29+
```
1530

16-
- **Naming**: snake_case for functions/variables, PascalCase for classes
17-
- **Type Hints**: Mandatory throughout (enforced by mypy if enabled)
18-
- **Async**: All I/O operations use `async`/`await`
19-
- **Testing**: pytest with fixtures and async support
20-
- **Formatting**: black (opinionated), flake8 (linting)
31+
## Stack
2132

22-
## 🏗️ Architecture at a Glance
33+
- Python 3.13.3 (`.python-version` - auto-detected by pyenv/asdf/mise)
34+
- FastAPI 0.128.6, Uvicorn
35+
- SQLite + SQLAlchemy 2.0 (async) + aiosqlite
36+
- pytest + pytest-cov + httpx
37+
- Flake8 + Black
38+
- aiocache (in-memory, 10min TTL)
39+
40+
## Architecture
2341

2442
```text
25-
Route → Service → Database
26-
↓ ↓
27-
Cache Session
43+
Request → Routes → Services → SQLAlchemy → SQLite
44+
(API) (Logic) (Async ORM) (Storage)
45+
46+
Pydantic (Validation)
2847
```
2948

30-
- **Routes**: FastAPI endpoints with dependency injection
31-
- **Services**: Async database operations via SQLAlchemy
32-
- **Database**: SQLite with async support (`aiosqlite`)
33-
- **Models**: Pydantic for validation, SQLAlchemy for ORM
34-
- **Cache**: aiocache SimpleMemoryCache (TTL: 600s / 10 min)
49+
**Key Directories:**
50+
- `routes/` - API endpoints (player_route.py, health_route.py)
51+
- `services/` - Business logic (player_service.py)
52+
- `models/` - Pydantic validation (camelCase JSON API)
53+
- `schemas/` - SQLAlchemy ORM models
54+
- `databases/` - Async DB setup, session factory
55+
- `storage/` - SQLite file (pre-seeded, 26 players)
56+
- `tests/` - pytest suite (test_main.py, conftest.py)
57+
58+
**Config Files:**
59+
- `.flake8` - Linter (max-line-length=88, complexity=10)
60+
- `pyproject.toml` - Black formatter (line-length=88)
61+
- `.coveragerc` - Coverage config (80% target)
62+
- `compose.yaml` - Docker orchestration
63+
- `Dockerfile` - Multi-stage build
64+
65+
## API Endpoints
66+
67+
All async with `AsyncSession` injection:
68+
- `POST /players/` → 201|409|422
69+
- `GET /players/` → 200 (cached 10min)
70+
- `GET /players/{player_id}` → 200|404
71+
- `GET /players/squadnumber/{squad_number}` → 200|404
72+
- `PUT /players/{player_id}` → 200|404|422
73+
- `DELETE /players/{player_id}` → 200|404
74+
- `GET /health` → 200
75+
76+
JSON: camelCase (e.g., `squadNumber`, `firstName`)
77+
78+
## CI/CD
79+
80+
**python-ci.yml** (push/PR to master):
81+
1. Lint: commitlint → `flake8 .``black --check .`
82+
2. Test: `pytest -v` → coverage
83+
3. Upload to Codecov
84+
85+
**python-cd.yml** (tags `v*.*.*-*`):
86+
1. Validate semver + coach name
87+
2. Run tests
88+
3. Build Docker (amd64/arm64)
89+
4. Push to GHCR (3 tags: semver/coach/latest)
90+
5. Create GitHub release
91+
92+
## Critical Patterns
93+
94+
### Async Everywhere
95+
```python
96+
# Always use async/await
97+
async def get_player(async_session: AsyncSession, player_id: int):
98+
stmt = select(Player).where(Player.id == player_id)
99+
result = await async_session.execute(stmt)
100+
return result.scalar_one_or_none()
101+
```
102+
- All routes: `async def`
103+
- Database: `AsyncSession` (never `Session`)
104+
- Driver: `aiosqlite` (not `sqlite3`)
105+
- SQLAlchemy 2.0: `select()` (not `session.query()`)
106+
107+
### camelCase API Contract
108+
```python
109+
class PlayerModel(BaseModel):
110+
model_config = ConfigDict(alias_generator=to_camel)
111+
squad_number: int # Python: snake_case
112+
# JSON API: "squadNumber" (camelCase)
113+
```
35114

36-
## ✅ Copilot Should
115+
### Database Schema Changes
116+
⚠️ No Alembic yet - manual process:
117+
1. Update `schemas/player_schema.py`
118+
2. Manually update `storage/players-sqlite3.db` (SQLite CLI/DB Browser)
119+
3. Preserve 26 players
120+
4. Update `models/player_model.py` if API changes
121+
5. Update services + tests
37122

38-
- Generate idiomatic async FastAPI code with proper type hints
39-
- Use SQLAlchemy async APIs (`select()`, `scalars()`, `session.commit()`)
40-
- Follow dependency injection pattern with `Depends()`
41-
- Write tests with pytest async fixtures
42-
- Apply Pydantic models for request/response validation
43-
- Use structured logging (avoid print statements)
44-
- Implement proper HTTP status codes and responses
123+
### Caching
124+
- Key: `"players"` (hardcoded)
125+
- TTL: 600s (10min)
126+
- Cleared on POST/PUT/DELETE
127+
- Header: `X-Cache` (HIT/MISS)
45128

46-
## 🚫 Copilot Should Avoid
129+
## Common Issues
47130

48-
- Synchronous database operations
49-
- Mixing sync and async code
50-
- Missing type hints on functions
51-
- Using `print()` instead of logging
52-
- Creating routes without caching consideration
53-
- Ignoring Pydantic validation
131+
1. **SQLAlchemy errors** → Always catch + rollback in services
132+
2. **Test file**`test_main.py` excluded from Black (preserves long names)
133+
3. **Database location** → Local: `./storage/`, Docker: `/storage/` (volume)
134+
4. **Pydantic validation** → Returns 422 (not 400)
135+
5. **Import order** → stdlib → third-party → local
54136

55-
## ⚡ Quick Commands
137+
## Validation Checklist
56138

57139
```bash
58-
# Run with hot reload
59-
uvicorn main:app --reload --host 0.0.0.0 --port 9000
140+
flake8 . # Must pass
141+
black --check . # Must pass
142+
pytest # All pass
143+
pytest --cov=./ --cov-report=term # ≥80%
144+
curl http://localhost:9000/players # 200 OK
145+
```
60146

61-
# Test with coverage
62-
pytest --cov=. --cov-report=term-missing
147+
## Code Conventions
63148

64-
# Docker
65-
docker compose up
149+
- Files: snake_case
150+
- Functions/vars: snake_case
151+
- Classes: PascalCase
152+
- Type hints: Required everywhere
153+
- Logging: `logging` module (never `print()`)
154+
- Errors: Catch specific exceptions
155+
- Line length: 88
156+
- Complexity: ≤10
66157

67-
# Swagger: http://localhost:9000/docs
68-
```
158+
## Commit Messages
69159

70-
## 📚 Need More Detail?
160+
Follow Conventional Commits format (enforced by commitlint in CI):
71161

72-
**For operational procedures**: Load `#file:AGENTS.md`
73-
**For Docker expertise**: *(Planned)* `#file:SKILLS/docker-containerization/SKILL.md`
74-
**For testing patterns**: *(Planned)* `#file:SKILLS/testing-patterns/SKILL.md`
162+
**Format:** `type(scope): description (#issue)`
163+
164+
**Rules:**
165+
- Max 80 characters
166+
- Types: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`, `ci`, `perf`, `style`, `build`
167+
- Scope: Optional (e.g., `api`, `db`, `service`, `route`)
168+
- Issue number: Required suffix
169+
170+
**Examples:**
171+
```text
172+
feat(api): add player stats endpoint (#42)
173+
fix(db): resolve async session leak (#88)
174+
```
75175

76-
---
176+
**CI Check:** First step in python-ci.yml validates all commit messages
77177

78-
💡 **Why this structure?** Copilot auto-loads this file on every chat (~500 tokens). Loading `AGENTS.md` or `SKILLS/` explicitly gives you deep context only when needed, saving 80% of your token budget!
178+
Trust these instructions. Search codebase only if info is incomplete/incorrect.

.github/workflows/python-cd.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ jobs:
9595
uses: docker/setup-buildx-action@v3.12.0
9696

9797
- name: Build and push Docker image to GitHub Container Registry
98-
uses: docker/build-push-action@v6.18.0
98+
uses: docker/build-push-action@v6.19.1
9999
with:
100100
context: .
101101
push: true

0 commit comments

Comments
 (0)