Development Workflow¶
Detailed guide for developing features and fixing bugs in Genji Shimada.
Setting Up Your Environment¶
1. Fork the Repository¶
Click "Fork" on GitHub to create your own copy:
2. Clone Your Fork¶
3. Add Upstream Remote¶
4. Install Dependencies¶
5. Configure Environment¶
Copy the local environment template:
Edit .env.local with your Discord bot token and other settings.
6. Start Infrastructure¶
This starts PostgreSQL, RabbitMQ, and MinIO for local development.
Daily Workflow¶
1. Sync with Upstream¶
Before starting work, sync your fork:
2. Create a Feature Branch¶
Branch naming conventions:
feature/- New featuresfix/- Bug fixesdocs/- Documentation changesrefactor/- Code refactoringtest/- Test improvements
3. Make Changes¶
Edit code, add tests, update documentation.
4. Run Linters¶
This runs:
- Ruff (formatting and linting)
- BasedPyright (type checking)
Fix linting issues:
5. Run Tests¶
For faster iteration, run specific tests:
# API tests only
just test-api
# Specific test file
uv run pytest apps/api/tests/test_maps.py
# Specific test function
uv run pytest apps/api/tests/test_maps.py::test_get_map
6. Commit Changes¶
Write clear, descriptive commit messages:
git add .
git commit -m "feat: add map search endpoint
- Add /maps/search route with query parameters
- Implement full-text search using PostgreSQL
- Add tests for search functionality
"
Commit message format:
Types:
feat- New featurefix- Bug fixdocs- Documentationrefactor- Code refactoringtest- Test improvementschore- Maintenance tasks
7. Push to Your Fork¶
8. Open a Pull Request¶
- Go to your fork on GitHub
- Click "Compare & pull request"
- Fill out the PR template
- Submit the PR
Working on the API¶
Project Structure¶
apps/api/
├── di/ # Business logic (DI modules)
│ ├── base.py # BaseService with RabbitMQ publishing
│ ├── auth.py # Authentication logic
│ ├── maps.py # Map CRUD operations
│ └── completions.py # Completion tracking
├── routes/ # HTTP route handlers
│ ├── maps/ # Map-related routes
│ └── completions.py # Completion endpoints
├── middleware/ # Auth and request processing
│ ├── auth.py # CustomAuthenticationMiddleware
│ └── guards.py # Scope guards
├── utilities/ # Shared utilities
│ └── errors.py # Custom exceptions
└── tests/ # API tests
Adding a New Endpoint¶
- Define the route handler in
routes/:
from litestar import get
from genjishimada_sdk.maps import MapResponse
@get("/maps/{map_id:int}")
async def get_map(map_id: int, conn: Connection) -> MapResponse:
map_data = await maps_service.get_map(conn, map_id)
return map_data
- Implement business logic in
di/:
from asyncpg import Connection
from genjishimada_sdk.maps import MapResponse
async def get_map(conn: Connection, map_id: int) -> MapResponse:
row = await conn.fetchrow(
"SELECT * FROM maps.maps WHERE id = $1",
map_id
)
if not row:
raise CustomHTTPException(404, "Map not found")
return MapResponse(**row)
- Add tests in
tests/:
import pytest
from httpx import AsyncClient
@pytest.mark.asyncio
async def test_get_map(client: AsyncClient):
response = await client.get("/api/v3/maps/1")
assert response.status_code == 200
- Run tests:
Database Queries¶
Use AsyncPG with parameterized queries:
# Good - parameterized query
row = await conn.fetchrow(
"SELECT * FROM maps.maps WHERE id = $1",
map_id
)
# Bad - SQL injection risk
row = await conn.fetchrow(
f"SELECT * FROM maps.maps WHERE id = {map_id}"
)
Publishing Messages¶
Use BaseService.publish_message():
from di.base import BaseService
from genjishimada_sdk.completions import CompletionCreatedEvent
class CompletionHandler(BaseService):
async def create_completion(self, user_id: int, map_id: int) -> int:
# Create in database
completion_id = await insert_completion(...)
# Publish event
event = CompletionCreatedEvent(
completion_id=completion_id,
)
await self.publish_message(
queue_name="api.completion.submission",
message=event,
message_id=f"completion-{completion_id}",
)
return completion_id
Working on the Bot¶
Project Structure¶
apps/bot/
├── core/
│ └── genji.py # Main bot class
├── extensions/ # Feature modules (cogs)
│ ├── rabbit.py # RabbitMQ service
│ ├── api_service.py # API client
│ └── completions.py # Completion handlers
└── configs/ # Configuration files
├── dev.toml
└── prod.toml
Adding a Queue Consumer¶
- Define the consumer in an extension:
from extensions._queue_registry import queue_consumer
from genjishimada_sdk.completions import CompletionCreatedEvent
@queue_consumer(
"api.completion.submission",
struct_type=CompletionCreatedEvent,
idempotent=True
)
async def handle_completion(
self,
event: CompletionCreatedEvent,
message: AbstractIncomingMessage,
) -> None:
# Send Discord notification
channel = self.bot.get_channel(COMPLETION_CHANNEL_ID)
await channel.send(f"New completion: {event.completion_id}")
- Test manually:
Trigger the event from the API and verify the bot processes it.
Calling the API¶
Use the shared APIService attached to the bot:
Working on the SDK¶
Adding a New Model¶
- Define the model in
libs/sdk/src/genjishimada_sdk/:
import msgspec
class Achievement(msgspec.Struct):
id: int
name: str
description: str
icon_url: str | None
xp_reward: int
- Export from
__init__.py:
- Use in API and bot:
from genjishimada_sdk import Achievement
achievement = Achievement(
id=1,
name="First Completion",
description="Complete your first map",
icon_url=None,
xp_reward=100,
)
- Run linters:
Debugging¶
API Debugging¶
- Add debug logging:
import logging
logger = logging.getLogger(__name__)
logger.debug(f"Processing completion: {completion_id}")
- Run with debug mode:
- Check logs:
Logs appear in the terminal.
Bot Debugging¶
- Add debug logging:
- Run the bot:
- Check logs:
Logs appear in the terminal.
Database Debugging¶
- Connect to PostgreSQL:
- Run queries:
Common Issues¶
Import Errors¶
If you see import errors, re-sync dependencies:
Database Connection Failed¶
Ensure Docker services are running:
Tests Failing¶
Run tests with verbose output:
Type Errors¶
Run BasedPyright to see all type errors:
Next Steps¶
- Contributing Guide - General contribution guidelines
- Documentation Guide - Update documentation
- Bot Architecture - Understand the bot
- API Documentation - Explore the API