Skip to content

Latest commit

 

History

16 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

notifyhub (Phase 2)

CI

Architecture

Client ↓ FastAPI ↓ PostgreSQL ↓ Redis Queue ↓ Celery Worker ↓ Notification Processor (EmailProvider)

Endpoints

  • POST /api/v1/notifications
    • Creates notification record
    • Enqueues Celery job
    • Returns HTTP 202:
{
  "success": true,
  "message": "Notification queued",
  "notification_id": "<uuid>"
}
  • GET /api/v1/notifications/{id}
    • Returns status and persisted attempts

Retry schedule

  • Attempt #1: immediately
  • Attempt #2: after 30 seconds
  • Attempt #3: after 60 seconds

If all retries fail:

  • Notification status becomes FAILED
  • The final error is stored in notification_attempts.

Starting services (Docker)

From repo root:

docker-compose -f backend/docker-compose.yml up --build

Run migrations:

docker-compose -f backend/docker-compose.yml exec backend alembic upgrade head

Worker

The Celery worker is started via docker-compose.

Logs will include:

  • Notification Queued
  • Notification Processing Started
  • Retry Attempt
  • Notification Sent
  • Notification Failed

Continuous Integration (Phase 8)

A GitHub Actions workflow runs automatically on every push and pull request to main or master.

Pipeline diagram:

Push / Pull Request
↓
GitHub Actions
↓
Ruff (lint)
↓
Black (format check)
↓
Pytest (unit & integration tests)
↓
Docker Build (build integrity)
↓
✅ Success

What runs in CI

Step Tool Purpose
Lint Ruff Enforce Python coding standards, detect unused imports, bug patterns
Format Black Ensure consistent code formatting across the entire codebase
Test Pytest Run all backend tests (async, auth, notifications, templates, tracing, metrics, etc.)
Build Docker Verify the Dockerfile builds successfully without errors

Why each tool is used

  • Ruff: Fastest Python linter. Replaces Flake8, isort, and pyflakes. Catches common bugs, unused imports, and style violations. Runs in CI as ruff check . and fails on any lint error.
  • Black: Zero-configuration code formatter. Eliminates debates about code style. Runs in CI as black --check . — it does not modify files; it only verifies formatting is correct.
  • Pytest: Standard Python test runner. Runs all tests in the backend/tests/ directory with async support. Fails the pipeline if any test fails.
  • Docker: Builds the backend/Dockerfile to verify the image compiles correctly. This catches missing dependencies or syntax errors early, before deployment.

Local verification before pushing

Developers should run these commands from the backend/ directory before pushing:

# Install development dependencies (includes Black, Ruff, Pytest)
pip install -r requirements-dev.txt

# Lint
ruff check .

# Format check
black --check .

# Run tests with coverage and JUnit report
pytest --junitxml=test-results.xml --cov=app --cov-report=term --cov-report=xml

# Build Docker image
docker build -f Dockerfile -t notifyhub:test .

These commands match the CI pipeline exactly. Passing them locally guarantees the CI run will pass.

Workflow file

The CI workflow is defined in .github/workflows/ci.yml. It uses:

  • actions/checkout@v4 — checkout repository
  • actions/setup-python@v5 — Python 3.13 with pip caching
  • actions/cache@v4 — Docker layer caching for faster builds
  • docker/setup-buildx-action@v3 — BuildKit for efficient Docker builds

Jobs run sequentially:

  1. lint — Ruff + Black
  2. test — Pytest with coverage + JUnit
  3. docker — Docker build with Buildx cache

Extending the workflow

To add deployment later, create a new job:

deploy:
  needs: docker
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - run: echo "Deploy step here"

The sequential job chain (lint -> test -> docker -> deploy) keeps the pipeline modular and easy to extend without rewriting existing jobs.

Test Coverage (Phase 8.3)

Test coverage measures how much of the application source code (backend/app/) is exercised by the test suite. Coverage is generated automatically in CI and can be run locally.

Running coverage locally

From the backend/ directory:

pytest --cov=app --cov-report=term --cov-report=xml

This generates two files:

  • coverage.xml — Machine-readable XML report (used by CI artifact uploads)
  • test-results.xml — JUnit XML report (test pass/fail details)

The terminal output includes a per-module breakdown:

---------- coverage: platform linux, python 3.13.0-final-0 -----------
Name                                     Stmts   Miss  Cover
------------------------------------------------------------
app/api/v1/routes_auth.py                   25      0   100%
app/api/v1/routes_notifications.py          20      2    90%
app/core/config.py                          10      0   100%
...
TOTAL                                      500     50    90%

CI coverage

In GitHub Actions, the pytest step runs with --cov=app --cov-report=term --cov-report=xml. The generated coverage.xml is uploaded as a build artifact for download after the pipeline completes.

Generated files (both local and CI)

File Format Purpose
coverage.xml XML (Cobertura) Machine-readable coverage data for CI artifact storage
test-results.xml XML (JUnit) Test pass/fail details for CI artifact storage

Future improvements (not implemented)

  • Coverage thresholds: Fail the build if coverage drops below a configurable percentage.
  • Codecov / Coveralls: Upload coverage reports to a hosted dashboard for historical tracking.
  • HTML reports: Generate an interactive HTML report for detailed line-by-line coverage exploration.
  • Diff coverage: Report coverage only on lines changed in a pull request.

Pre-commit Hooks (Phase 8.2)

Pre-commit hooks automatically run Ruff and Black before every git commit, catching formatting and linting issues before code reaches GitHub.

Hooks configured

Hook ID Behaviour
Ruff ruff Lint check (ruff check --no-fix). Fails on lint errors but does not modify code
Black black Auto-formats Python files to match CI expectation
End-of-file fixer end-of-file-fixer Ensures every file ends with a trailing newline
Trailing whitespace trailing-whitespace Removes trailing whitespace from lines
YAML validation check-yaml Validates all YAML files (GitHub Actions workflows, Docker Compose, configs). Prevents broken configuration from being committed
Merge conflict detection check-merge-conflict Scans for unresolved merge conflict markers (<<<<<<<, >>>>>>>, =======). Prevents accidental commits containing conflicts

Why these matter

  • YAML validation: Catches syntax errors in .yml/.yaml files before they break CI pipelines or Docker Compose. A missing colon or incorrect indentation fails the commit immediately.
  • Merge conflict detection: After resolving a conflicted merge, it is easy to miss leftover conflict markers. This hook scans every file and blocks the commit if any unresolved markers remain.

Installation

cd backend
pip install -r requirements-dev.txt
pre-commit install

After installation, hooks run automatically before every commit.

Manual execution

To run all hooks against all files without committing:

pre-commit run --all-files

To run a specific hook:

pre-commit run ruff --all-files
pre-commit run black --all-files

Consistency with CI

The pre-commit hooks use the exact same tools and versions as CI:

Tool CI command Pre-commit command
Ruff ruff check . ruff check --no-fix
Black black --check . black (auto-fixes)

The difference is that pre-commit auto-fixes formatting (Black) while CI only checks. This is intentional: pre-commit catches issues before commit, CI enforces after push.

Skipping hooks

In an emergency, skip pre-commit hooks:

git commit -m "urgent fix" --no-verify

This should be rare — passing pre-commit locally guarantees the lint and format steps in CI will pass.

Observability (Phase 7D — Jaeger & Docker Integration)

Architecture

Traces flow through the system as follows:

Client
↓
FastAPI
↓
Redis
↓
PostgreSQL
↓
Celery
↓
Worker
↓
Provider
↓
OpenTelemetry
↓
Jaeger

Running the complete stack

Start all services including Jaeger:

docker-compose -f backend/docker-compose.yml up --build

This starts:

  • postgres: Database
  • redis: Message broker for Celery
  • jaeger: Distributed tracing backend (all-in-one)
  • backend: FastAPI application
  • worker: Celery worker

Run migrations:

docker-compose -f backend/docker-compose.yml exec backend alembic upgrade head

Access URLs

About

Production-grade multi-channel notification platform built with FastAPI, PostgreSQL, Redis, Celery, and OpenTelemetry.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages