Skip to content

Latest commit

 

History

History
294 lines (200 loc) · 10.4 KB

development.md

File metadata and controls

294 lines (200 loc) · 10.4 KB

Oblidog Ledger - Development

Current Assumptions

  • This is a private application.
  • Public signup is disabled.
  • Users are admin-managed only.
  • Local backend workflow is based on uv.
  • Local frontend workflow is based on Bun.
  • The application is expected to connect to the external PostgreSQL configuration from .env.

Docker Compose

  • Start the local stack with Docker Compose:
docker compose watch
  • Now you can open your browser and interact with these URLs:

Frontend, built with Docker, with routes handled based on the path: http://localhost:5173

Backend, JSON based web API based on OpenAPI: http://localhost:8000

Automatic interactive documentation with Swagger UI (from the OpenAPI backend): http://localhost:8000/docs

Adminer, database web administration: http://localhost:8080

Traefik UI, to see how the routes are being handled by the proxy: http://localhost:8090

Note: The first time you start your stack, it might take a minute for it to be ready. While the backend waits for the database to be ready and configures everything. You can check the logs to monitor it.

To check the logs, run (in another terminal):

docker compose logs

To check the logs of a specific service, add the name of the service, e.g.:

docker compose logs backend

Mailpit

Mailpit is a simple SMTP server that catches all emails sent by the backend during local development. Instead of sending real emails, they are captured and displayed in a web interface.

This is useful for:

  • Testing email functionality during development
  • Verifying email content and formatting
  • Debugging email-related functionality without sending real emails

The backend is automatically configured to use Mailpit when running with Docker Compose locally (SMTP on port 1025). All captured emails can be viewed at http://localhost:8025.

For a host backend (make dev-b), start only docker compose up -d --wait mailpit and set SMTP_HOST=localhost, SMTP_PORT=1025, SMTP_TLS=false, SMTP_SSL=false, empty SMTP_USER/SMTP_PASSWORD, and EMAILS_FROM_EMAIL=noreply@example.com in your local environment. Never start the default Compose backend alongside make dev-b.

To preview report emails, use a disposable local development database and enable Daily report or Weekly report in the test user's settings. Trigger System Run from the admin interface (weekly reports run on Monday), then inspect the HTML and text versions in Mailpit. Scheduled reports record delivery, so repeat runs for the same reporting period may be skipped. Mailpit has no external SMTP relay configured; captured messages stay local.

Email E2E tests use make e2e, which runs the dedicated compose.e2e.yml stack with its own database, SMTP capture service, and no published backend port. MAILPIT_HOST=http://mailpit:8025 selects the capture API inside that stack. Helpers search by recipient and subject, stop after five seconds (including hung HTTP requests), and extract links from message HTML without opening the viewer. Browser navigation uses Playwright's configured application base URL. Run helper regressions alone with cd frontend && bun test ./tests/utils/mailpit.unit.ts.

Email design system

React Email preview/export commands, runtime Jinja integration and synthetic fixture previews are documented in emails/README.md. Run bun run email:dev for the component preview or render the Python fixture matrix with uv run --locked python emails/scripts/render_previews.py. Generated HTML/text are committed and checked for freshness in CI.

Local Development

The Docker Compose files are configured so that each of the services is available in a different port in localhost.

For the backend and frontend, they use the same port that would be used by their local development server, so, the backend is at http://localhost:8000 and the frontend at http://localhost:5173.

This way, you could turn off a Docker Compose service and start its local development service, and everything would keep working, because it all uses the same ports.

For example, you can stop that frontend service in the Docker Compose, in another terminal, run:

docker compose stop frontend

And then start the local frontend development server:

bun run --filter frontend dev

Or you could stop the backend Docker Compose service:

docker compose stop backend

And then you can run the local development server for the backend:

cd backend
fastapi dev app/main.py

Docker Compose files and env vars

There is a main compose.yml file with all the configurations that apply to the whole stack, it is used automatically by docker compose.

And there's also a compose.override.yml with overrides for development, for example to mount the source code as a volume. It is used automatically by docker compose to apply overrides on top of compose.yml.

These Docker Compose files use the .env file containing configurations to be injected as environment variables in the containers.

They also use some additional configurations taken from environment variables set in the scripts before calling the docker compose command.

After changing variables, make sure you restart the stack:

docker compose watch

The .env file

The .env file is the one that contains all your configurations, generated keys and passwords, etc.

Depending on your workflow, you could want to exclude it from Git, for example if your project is public. In that case, you would have to make sure to set up a way for your CI tools to obtain it while building or deploying your project.

One way to do it could be to add each environment variable to your CI/CD system, and updating the compose.yml file to read that specific env var instead of reading the .env file.

Python version

The backend uses Python 3.14 in Docker, CI, local development, and the Vercel demo. The root .python-version pins the uv workspace interpreter; backend/.python-version also pins deployments with backend/ as their project root.

Install the interpreter and sync the workspace from the repository root:

uv python install 3.14
uv sync --frozen

An existing virtual environment created with an older Python version will be recreated by uv when syncing. The host system Python does not determine the interpreter inside the backend Docker image.

Pre-commit and Commitizen

The repository uses .pre-commit-config.yaml for local git hooks and commitizen for conventional commits.

Configured hooks currently cover:

  • frontend biome check
  • backend ruff check
  • backend ruff format
  • backend mypy
  • frontend SDK regeneration when backend API files change
  • commitizen commit message validation on the commit-msg hook

The Backend Quality GitHub Actions workflow also runs backend/scripts/lint.sh on every pull request, including release and back-sync PRs. It checks all backend Python files with strict mypy, then checks lint and formatting across the backend with Ruff. The check fails on errors and does not require a database or apply fixes.

Ruff also checks timezone-aware date/time usage, logging, and explicit exception chaining. FastAPI parameter markers and the integration dependency factory are allowed in default arguments; other calls remain checked. Alembic migrations are excluded. Naive datetime fixtures are allowed only in the legacy importer test module that deliberately covers old workbook values.

Run the same checks locally from backend/:

uv run --locked bash scripts/lint.sh

Install hooks

The backend dev dependencies include both pre-commit and commitizen.

After syncing backend dependencies, install the hooks from backend/:

uv sync
uv run pre-commit install --hook-type pre-commit --hook-type commit-msg

Now normal git commit runs the pre-commit hooks, and the final commit message is validated by commitizen.

Running hooks manually

To run all hooks on the current repository:

uv run pre-commit run --all-files

To validate or create conventional commit messages manually:

uv run cz check --message "feat: add example"
uv run cz commit

From the repository root you can use the helper wrapper:

bash ./scripts/cz.sh check --message "feat: add example"
bash ./scripts/cz.sh commit

There is also a root Makefile for common shortcuts:

make cmt
make dev-b
make dev-f
make pre
make test
make cov

URLs

The production or staging URLs would use these same paths, but with your own domain.

Development URLs

Development URLs, for local development.

Frontend: http://localhost:5173

Backend: http://localhost:8000

Automatic Interactive Docs (Swagger UI): http://localhost:8000/docs

Automatic Alternative Docs (ReDoc): http://localhost:8000/redoc

Adminer: http://localhost:8080

Traefik UI: http://localhost:8090

Mailpit: http://localhost:8025

Codecov coverage reports

The existing backend and frontend unit-test workflows upload separate backend and frontend reports to Codecov after successful tests. Backend XML uses the same coverage configuration and exclusions as the existing HTML report; the 85% backend gate remains enforced by coverage report --fail-under=85. Frontend coverage comes from Bun unit tests and covers the files exercised by those tests, not the entire React UI or Playwright scenarios. Codecov statuses are informational and PR comments are disabled. An upload failure does not fail the test job.

To enable the service, sign in to Codecov with GitHub and grant its GitHub App access to oblidog/oblidog-ledger. Uploads authenticate through GitHub OIDC with job-scoped id-token: write; no CODECOV_TOKEN secret is needed. Fork PR uploads use Codecov's tokenless handling instead of OIDC.

Tests still run on pull requests, respecting the existing release/back-sync skip policy. No extra automatic test run is added on merge or release. To create an initial baseline on dev or main, run both Test Backend and Frontend Unit Tests manually from the Actions tab using that branch. Repeat the manual runs when a current base-branch report is needed for comparisons. The workflows must exist on the default branch before GitHub exposes their manual-run controls.

Generate the same reports locally:

# Backend, after the usual test run (from backend/)
uv run coverage xml -o coverage.xml

# Frontend (from frontend/)
bun run test:unit --coverage --coverage-reporter=lcov