Contributing to Forgetful
November 22, 2025 · View on GitHub
This guide covers testing and deployment workflows for contributors.
Running with Docker with source
This will approach will fall back to using the build and as such allow you to check any changes you have made inside of a container.
git clone https://github.com/ScottRBK/forgetful.git
cd forgetful
cd docker
docker compose up -d --build
Optional: Customize configuration by copying the example environment file:
cd docker
cp .env.example .env
# Edit .env with your custom values
docker compose up -d
Testing Philosophy
We focus on integration and E2E tests over unit tests. Tests should cover critical workflows without exhaustive edge case coverage.
Integration Tests
Location: tests/integration/
Purpose: Test business logic with stubbed I/O (no real database required)
Run locally:
pytest tests/integration/
These tests use in-memory stubs and run fast (~seconds). They form the bulk of our test suite and catch 90% of issues.
End-to-End Tests
SQLite E2E Tests
Location: tests/e2e_sqlite/
Purpose: Test complete stack with in-memory SQLite
Requirements: None (no Docker required)
Run locally:
pytest tests/e2e_sqlite/
CI Status: ✅ Runs automatically in CI workflow on every push/PR
These tests use an in-memory SQLite database for test isolation. Fast execution (~30 seconds for 94 tests) with automatic cleanup. They provide full-stack coverage without Docker dependencies, making them ideal for CI.
PostgreSQL E2E Tests
Location: tests/e2e/
Purpose: Test complete stack with real PostgreSQL
Requirements: PostgreSQL running in Docker
Run locally:
# Start PostgreSQL
docker compose up -d postgres
# Run E2E tests
pytest -m e2e
CI Status: ⚠️ Only runs on push to main branch or manual workflow dispatch
These tests are marked with @pytest.mark.e2e and validate the full application stack with PostgreSQL backend. They're heavier than SQLite E2E tests and reserved for main branch validation.
Linting
linting with ruff and uv
uv tool run ruff check .
CI/CD Workflows
ci.yml - Continuous Integration
Triggers: Every push and pull request
Purpose: Fast feedback for contributors
Steps:
- Run integration tests (stubbed, no Docker)
- Run SQLite E2E tests (in-memory SQLite, full stack)
- Run linting
Status: Blocks PR merge if fails ✅
Test Coverage: Integration tests validate business logic with stubs. SQLite E2E tests validate the complete application stack including real database interactions, all without Docker dependencies.
e2e.yml - PostgreSQL E2E Validation
Triggers: Push to main branch OR manual workflow dispatch
Purpose: Validate with real PostgreSQL database
Steps:
- Run PostgreSQL E2E tests (
tests/e2e/with-m e2emarker)
Status: Reports failures but doesn't block ⚠️
Test Coverage: Validates full application stack with PostgreSQL backend. These are heavier tests reserved for main branch validation to ensure production database compatibility.
build.yml - Docker Image Build
Triggers: Version tag push only (e.g., v0.1.0, v1.0.0)
Purpose: Build and publish release images
Steps:
- Build Docker image
- Tag with semver, SHA, and
latest - Push to GitHub Container Registry (
ghcr.io/scottrbk/forgetful)
Note: Only runs when you manually create a release tag
Creating a Release
- Commit your changes to
mainbranch - Create and push a version tag:
git tag v0.1.0 git push origin v0.1.0 - Build workflow automatically creates Docker image
Deployment
Workflow: deploy.yml
Purpose: Deploy to staging/production environments
Method: Self-hosted runners with sparse checkout
Environment Selection: Runners match environment by label (e.g., staging runner for staging deployment)