From 3db0135d992b02fed6f93fcdb5106990997f150c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Apr 2026 08:03:20 +0000 Subject: [PATCH 01/30] chore(deps): bump python from 3.12-slim to 3.14-slim Bumps python from 3.12-slim to 3.14-slim. --- updated-dependencies: - dependency-name: python dependency-version: 3.14-slim dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index c29e45c..5d303df 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ # ============================================================================= # ── Stage 1: Builder ────────────────────────────────────────────────────────── -FROM python:3.12-slim AS builder +FROM python:3.14-slim AS builder WORKDIR /build @@ -19,7 +19,7 @@ RUN pip install --upgrade pip && \ pip install --no-cache-dir --prefix=/install -r requirements.txt # ── Stage 2: Runtime ────────────────────────────────────────────────────────── -FROM python:3.12-slim AS runtime +FROM python:3.14-slim AS runtime LABEL maintainer="donny-devops" \ org.opencontainers.image.title="docker-flask-postgres-api" \ From 656954b30d523c5b1a319bf72708191a38d33bf4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Apr 2026 08:03:24 +0000 Subject: [PATCH 02/30] chore(deps): bump python-dotenv from 1.0.1 to 1.2.2 Bumps [python-dotenv](https://github.com/theskumar/python-dotenv) from 1.0.1 to 1.2.2. - [Release notes](https://github.com/theskumar/python-dotenv/releases) - [Changelog](https://github.com/theskumar/python-dotenv/blob/main/CHANGELOG.md) - [Commits](https://github.com/theskumar/python-dotenv/compare/v1.0.1...v1.2.2) --- updated-dependencies: - dependency-name: python-dotenv dependency-version: 1.2.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 13eee6d..211350a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ Flask-Migrate==4.0.7 psycopg2-binary==2.9.9 # ── Config ──────────────────────────────────────────────────────────────────── -python-dotenv==1.0.1 +python-dotenv==1.2.2 # ── Serialization / Validation ──────────────────────────────────────────────── marshmallow==3.26.2 From 3473ff6d0a7192af2172d39d60e65db5fffdac1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 08:03:12 +0000 Subject: [PATCH 03/30] chore(deps): bump actions/upload-artifact from 4 to 7 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7be5d57..f7b0cc6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,7 +85,7 @@ jobs: pytest --cov=app --cov-report=xml --cov-fail-under=85 -v - name: Upload coverage - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: coverage-report path: coverage.xml From da7bf7a050933aa26bbb7165457b77fbe8c4d0fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 08:03:15 +0000 Subject: [PATCH 04/30] chore(deps): bump actions/setup-python from 5 to 6 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7be5d57..53b5600 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: ref: ${{ github.head_ref }} token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.12" cache: pip @@ -64,7 +64,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.12" cache: pip From fbcef6dc62a86163b2748c04a792ba42da3c0af2 Mon Sep 17 00:00:00 2001 From: Donny <271941740+donny-devops@users.noreply.github.com> Date: Sun, 17 May 2026 13:25:10 -0400 Subject: [PATCH 05/30] fix(api): make Marshmallow validators compatible with newer callbacks --- app/schemas.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/schemas.py b/app/schemas.py index ab50c4c..ade9429 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -1,5 +1,7 @@ """Marshmallow schemas for request validation and response serialization.""" +from typing import Any + from marshmallow import Schema, ValidationError, fields, validate, validates @@ -17,7 +19,13 @@ class ItemCreateSchema(Schema): ) @validates("name") - def validate_name_not_blank(self, value: str) -> None: + def validate_name_not_blank(self, value: str, **_: Any) -> None: + """Reject whitespace-only names. + + Marshmallow passes callback metadata such as data_key in newer 3.x/4.x + releases. Accepting **_ keeps the schema compatible across supported + versions without weakening validation. + """ if not value.strip(): raise ValidationError("Name cannot be blank or whitespace.") @@ -33,7 +41,8 @@ class ItemUpdateSchema(Schema): is_active = fields.Bool(required=False) @validates("name") - def validate_name_not_blank(self, value: str) -> None: + def validate_name_not_blank(self, value: str, **_: Any) -> None: + """Reject whitespace-only names while allowing Marshmallow metadata.""" if not value.strip(): raise ValidationError("Name cannot be blank or whitespace.") From da30d6815f715d0bad1e97abd7106a0ef0fa4342 Mon Sep 17 00:00:00 2001 From: Donny <271941740+donny-devops@users.noreply.github.com> Date: Sun, 17 May 2026 13:25:40 -0400 Subject: [PATCH 06/30] feat(api): add readiness endpoint and structured error responses --- app/__init__.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index f083c81..ca3e848 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,4 +1,8 @@ -from flask import Flask +from http import HTTPStatus + +from flask import Flask, jsonify +from sqlalchemy import text +from werkzeug.exceptions import HTTPException from app.config import Config from app.extensions import db, migrate @@ -18,9 +22,63 @@ def create_app(config_class: type = Config) -> Flask: app.register_blueprint(api_bp, url_prefix="/api/v1") - # Health check endpoint (outside blueprint for simplicity) @app.get("/health") - def health() -> dict: # type: ignore[return] + def health() -> dict[str, str]: + """Liveness check that does not require the database.""" return {"status": "healthy", "service": "docker-flask-postgres-api"} + @app.get("/ready") + def ready(): # noqa: ANN202 + """Readiness check that verifies database connectivity.""" + try: + db.session.execute(text("SELECT 1")) + except Exception as exc: # noqa: BLE001 + app.logger.warning("Database readiness check failed: %s", exc) + return ( + jsonify( + { + "status": "unready", + "service": "docker-flask-postgres-api", + "database": "unavailable", + } + ), + HTTPStatus.SERVICE_UNAVAILABLE, + ) + return jsonify( + { + "status": "ready", + "service": "docker-flask-postgres-api", + "database": "ok", + } + ) + + @app.errorhandler(HTTPException) + def handle_http_exception(exc: HTTPException): # noqa: ANN202 + """Return consistent JSON for Flask/Werkzeug HTTP errors.""" + return ( + jsonify( + { + "error": exc.name, + "message": exc.description, + "status_code": exc.code, + } + ), + exc.code, + ) + + @app.errorhandler(Exception) + def handle_unexpected_exception(exc: Exception): # noqa: ANN202 + """Return a safe JSON response for unexpected server errors.""" + app.logger.exception("Unhandled application error: %s", exc) + return ( + jsonify( + { + "error": "Internal Server Error", + "message": "An unexpected server error occurred.", + "status_code": HTTPStatus.INTERNAL_SERVER_ERROR, + } + ), + HTTPStatus.INTERNAL_SERVER_ERROR, + ) + return app From 94ddfd66a7ad0b195e431a0f16016df387459b4d Mon Sep 17 00:00:00 2001 From: Donny <271941740+donny-devops@users.noreply.github.com> Date: Sun, 17 May 2026 13:26:11 -0400 Subject: [PATCH 07/30] fix(docker): add database wait loop before migrations --- entrypoint.sh | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index bb7c9d1..e2bb810 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,19 +1,57 @@ #!/bin/bash # ============================================================================= # Docker container entrypoint -# Runs Flask-Migrate DB upgrade, then starts gunicorn. +# Waits for database connectivity, runs Flask-Migrate upgrade, then starts gunicorn. # ============================================================================= set -euo pipefail +: "${DATABASE_URL:?DATABASE_URL must be set}" +: "${GUNICORN_WORKERS:=2}" +: "${GUNICORN_THREADS:=4}" +: "${DB_WAIT_TIMEOUT:=60}" + +wait_for_database() { + echo "[entrypoint] Waiting for database connectivity..." + python <<'PY' +import os +import sys +import time + +from sqlalchemy import create_engine, text +from sqlalchemy.exc import SQLAlchemyError + +url = os.environ["DATABASE_URL"] +timeout = int(os.environ.get("DB_WAIT_TIMEOUT", "60")) +deadline = time.monotonic() + timeout +last_error = "unknown" + +while time.monotonic() < deadline: + try: + engine = create_engine(url, pool_pre_ping=True) + with engine.connect() as conn: + conn.execute(text("SELECT 1")) + print("[entrypoint] Database is reachable.") + sys.exit(0) + except SQLAlchemyError as exc: + last_error = str(exc) + time.sleep(2) + +print(f"[entrypoint] Database did not become reachable within {timeout}s: {last_error}", file=sys.stderr) +sys.exit(1) +PY +} + +wait_for_database + echo "[entrypoint] Running Flask-Migrate upgrade..." flask db upgrade echo "[entrypoint] Starting gunicorn on 0.0.0.0:5000..." exec python -m gunicorn \ --bind 0.0.0.0:5000 \ - --workers "${GUNICORN_WORKERS:-2}" \ - --threads "${GUNICORN_THREADS:-4}" \ - --timeout 60 \ + --workers "${GUNICORN_WORKERS}" \ + --threads "${GUNICORN_THREADS}" \ + --timeout "${GUNICORN_TIMEOUT:-60}" \ --access-logfile - \ --error-logfile - \ "app:create_app()" From 6a43d124a40140e231080f1271bfc52bc2987e9c Mon Sep 17 00:00:00 2001 From: Donny <271941740+donny-devops@users.noreply.github.com> Date: Sun, 17 May 2026 13:26:39 -0400 Subject: [PATCH 08/30] fix(docker): harden compose healthchecks and defaults --- docker-compose.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3136c53..b56bbeb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,7 @@ services: volumes: - postgres_data:/var/lib/postgresql/data ports: - - "5432:5432" + - "127.0.0.1:5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-appuser} -d ${POSTGRES_DB:-appdb}"] interval: 10s @@ -30,17 +30,21 @@ services: FLASK_ENV: ${FLASK_ENV:-production} SECRET_KEY: ${SECRET_KEY:-change-me} DATABASE_URL: postgresql://${POSTGRES_USER:-appuser}:${POSTGRES_PASSWORD:-apppassword}@db:5432/${POSTGRES_DB:-appdb} + DB_WAIT_TIMEOUT: ${DB_WAIT_TIMEOUT:-60} + GUNICORN_WORKERS: ${GUNICORN_WORKERS:-2} + GUNICORN_THREADS: ${GUNICORN_THREADS:-4} + GUNICORN_TIMEOUT: ${GUNICORN_TIMEOUT:-60} ports: - "5000:5000" depends_on: db: condition: service_healthy healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:5000/health"] + test: ["CMD", "curl", "-f", "http://localhost:5000/ready"] interval: 30s timeout: 10s retries: 3 - start_period: 20s + start_period: 30s networks: - app-network @@ -48,12 +52,14 @@ services: image: dpage/pgadmin4:latest container_name: flask-api-pgadmin restart: unless-stopped + profiles: + - admin environment: PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-admin@example.com} PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin} PGADMIN_CONFIG_SERVER_MODE: "False" ports: - - "5050:80" + - "127.0.0.1:5050:80" depends_on: db: condition: service_healthy From 9dd56c7c59c68d758504700189982760fdac1d09 Mon Sep 17 00:00:00 2001 From: Donny <271941740+donny-devops@users.noreply.github.com> Date: Sun, 17 May 2026 13:27:02 -0400 Subject: [PATCH 09/30] fix(ci): make lint check-only and least-privilege --- .github/workflows/ci.yml | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7be5d57..70403d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,34 +9,29 @@ on: env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true +permissions: + contents: read + jobs: lint: - name: Lint (ruff) + name: Lint and format check runs-on: ubuntu-latest - permissions: - contents: write steps: - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - token: ${{ secrets.GITHUB_TOKEN }} - uses: actions/setup-python@v5 with: python-version: "3.12" cache: pip - - run: pip install ruff + - name: Install lint tools + run: pip install ruff - name: Lint with ruff run: ruff check . --output-format=github - - name: Format with ruff - run: | - ruff format . - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git config user.name "github-actions[bot]" - git diff --quiet || git commit -am "style: auto-format with ruff" && git push + - name: Check formatting with ruff + run: ruff format --check . test: name: Test (pytest + PostgreSQL) @@ -51,7 +46,7 @@ jobs: POSTGRES_PASSWORD: testpass POSTGRES_DB: testdb options: >- - --health-cmd pg_isready + --health-cmd "pg_isready -U testuser -d testdb" --health-interval 10s --health-timeout 5s --health-retries 5 @@ -61,9 +56,11 @@ jobs: env: DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb SECRET_KEY: test-secret + FLASK_APP: "app:create_app()" steps: - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: "3.12" @@ -75,14 +72,10 @@ jobs: pip install -r requirements.txt - name: Run migrations - run: | - flask db upgrade - env: - FLASK_APP: "app:create_app()" + run: flask db upgrade - name: Run pytest - run: | - pytest --cov=app --cov-report=xml --cov-fail-under=85 -v + run: pytest --cov=app --cov-report=xml --cov-fail-under=85 -v - name: Upload coverage uses: actions/upload-artifact@v4 From e860efd6b6dea6ff5af70ff8cfef72e57141a1bc Mon Sep 17 00:00:00 2001 From: Donny <271941740+donny-devops@users.noreply.github.com> Date: Sun, 17 May 2026 13:27:29 -0400 Subject: [PATCH 10/30] test(api): cover readiness validation and JSON error behavior --- tests/test_routes.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/test_routes.py b/tests/test_routes.py index fa39fe6..411580d 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -32,13 +32,29 @@ def client(app: Flask) -> FlaskClient: return app.test_client() -# ── Health Check ────────────────────────────────────────────────────────────── +# ── Health / Readiness ──────────────────────────────────────────────────────── class TestHealth: def test_health_returns_200(self, client: FlaskClient) -> None: resp = client.get("/health") assert resp.status_code == 200 assert resp.json["status"] == "healthy" + def test_ready_returns_200_when_database_is_available(self, client: FlaskClient) -> None: + resp = client.get("/ready") + assert resp.status_code == 200 + assert resp.json["status"] == "ready" + assert resp.json["database"] == "ok" + + +# ── Error Handling ──────────────────────────────────────────────────────────── +class TestErrors: + def test_not_found_returns_json(self, client: FlaskClient) -> None: + resp = client.get("/does-not-exist") + assert resp.status_code == 404 + assert resp.is_json + assert resp.json["error"] == "Not Found" + assert resp.json["status_code"] == 404 + # ── List Items ──────────────────────────────────────────────────────────────── class TestListItems: @@ -100,6 +116,8 @@ def test_get_existing(self, client: FlaskClient) -> None: def test_get_not_found(self, client: FlaskClient) -> None: resp = client.get("/api/v1/items/99999") assert resp.status_code == 404 + assert resp.is_json + assert resp.json["error"] == "Not Found" # ── Update Item ─────────────────────────────────────────────────────────────── @@ -110,6 +128,17 @@ def test_update_name(self, client: FlaskClient) -> None: assert resp.status_code == 200 assert resp.json["name"] == "new-name" + def test_update_blank_name(self, client: FlaskClient) -> None: + created = client.post("/api/v1/items", json={"name": "old-name"}).json + resp = client.put(f"/api/v1/items/{created['id']}", json={"name": " "}) + assert resp.status_code == 422 + + def test_update_duplicate_name(self, client: FlaskClient) -> None: + client.post("/api/v1/items", json={"name": "first"}) + created = client.post("/api/v1/items", json={"name": "second"}).json + resp = client.put(f"/api/v1/items/{created['id']}", json={"name": "first"}) + assert resp.status_code == 409 + def test_update_not_found(self, client: FlaskClient) -> None: resp = client.put("/api/v1/items/99999", json={"name": "x"}) assert resp.status_code == 404 From 04e13f73c1de6bb28ff9cdea11e907e071f2ab50 Mon Sep 17 00:00:00 2001 From: Donny <271941740+donny-devops@users.noreply.github.com> Date: Sun, 17 May 2026 13:28:08 -0400 Subject: [PATCH 11/30] docs(env): document runtime tuning variables --- .env.example | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 69e49ee..0c0283d 100644 --- a/.env.example +++ b/.env.example @@ -1,13 +1,19 @@ # Application FLASK_ENV=development -SECRET_KEY=change-me-in-production-use-secrets-manager +SECRET_KEY=replace-with-a-local-development-value # Database -DATABASE_URL=postgresql://appuser:apppassword@db:5432/appdb +DATABASE_URL=postgresql://appuser:replace-with-local-db-value@db:5432/appdb POSTGRES_USER=appuser -POSTGRES_PASSWORD=apppassword +POSTGRES_PASSWORD=replace-with-local-db-value POSTGRES_DB=appdb +DB_WAIT_TIMEOUT=60 -# pgAdmin +# Gunicorn +GUNICORN_WORKERS=2 +GUNICORN_THREADS=4 +GUNICORN_TIMEOUT=60 + +# pgAdmin - only starts with: docker compose --profile admin up -d PGADMIN_DEFAULT_EMAIL=admin@example.com -PGADMIN_DEFAULT_PASSWORD=admin +PGADMIN_DEFAULT_PASSWORD=replace-with-local-admin-value From 08cbfb14dea32618853bf092159107c936109106 Mon Sep 17 00:00:00 2001 From: Donny <271941740+donny-devops@users.noreply.github.com> Date: Sun, 17 May 2026 13:28:47 -0400 Subject: [PATCH 12/30] docs(readme): fill architecture and operational runbook gaps --- README.md | 84 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b15c16c..4134222 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Docker Flask Postgres API -A production-ready REST API built with **Flask**, **PostgreSQL**, and **Docker Compose** — complete with migrations, a full test suite, and a GitHub Actions CI pipeline. +A production-ready REST API built with **Flask**, **PostgreSQL**, and **Docker Compose** — complete with migrations, a full test suite, readiness checks, and a GitHub Actions CI pipeline. [![CI](https://github.com/donny-devops/docker-flask-postgres-api/actions/workflows/ci.yml/badge.svg)](https://github.com/donny-devops/docker-flask-postgres-api/actions/workflows/ci.yml) [![Python](https://img.shields.io/badge/Python-3.12-3776AB?style=flat-square&logo=python&logoColor=white)](https://www.python.org/) @@ -12,6 +12,22 @@ A production-ready REST API built with **Flask**, **PostgreSQL**, and **Docker C ## Architecture +```text +Client / curl / API consumer + | + v +Flask API container + - Gunicorn + - Flask application factory + - Marshmallow validation + - SQLAlchemy ORM + - Flask-Migrate / Alembic + | + v +PostgreSQL container +``` + +Docker Compose runs the API and database on an isolated bridge network. The API waits for database connectivity before running migrations, then starts Gunicorn. PostgreSQL and pgAdmin bind to localhost by default to avoid exposing admin surfaces on every interface. --- @@ -23,14 +39,20 @@ git clone https://github.com/donny-devops/docker-flask-postgres-api.git cd docker-flask-postgres-api cp .env.example .env -# 2. Start all services -docker compose up -d +# 2. Start API + database +docker compose up -d --build -# 3. Run migrations -docker compose exec api flask db upgrade - -# 4. Verify +# 3. Verify liveness and readiness curl http://localhost:5000/health +curl http://localhost:5000/ready +``` + +The container entrypoint waits for Postgres, runs `flask db upgrade`, and starts Gunicorn automatically. Manual migration commands are still useful for local development, but they are not required for the standard Compose startup path. + +To start pgAdmin as an optional admin tool: + +```bash +docker compose --profile admin up -d pgadmin ``` --- @@ -39,7 +61,8 @@ curl http://localhost:5000/health | Method | Endpoint | Description | |--------|----------|-------------| -| `GET` | `/health` | Health check | +| `GET` | `/health` | Liveness check; does not require database connectivity | +| `GET` | `/ready` | Readiness check; verifies database connectivity | | `GET` | `/api/v1/items` | List all active items | | `POST` | `/api/v1/items` | Create a new item | | `GET` | `/api/v1/items/:id` | Get item by ID | @@ -72,13 +95,17 @@ curl -X POST http://localhost:5000/api/v1/items \ | Variable | Default | Description | |----------|---------|-------------| | `FLASK_ENV` | `production` | Flask environment mode | -| `SECRET_KEY` | *(required)* | Flask secret key | +| `SECRET_KEY` | required | Flask secret key; use a real secret manager outside local dev | | `DATABASE_URL` | `postgresql://...` | Full PostgreSQL connection string | | `POSTGRES_USER` | `appuser` | PostgreSQL username | -| `POSTGRES_PASSWORD` | `apppassword` | PostgreSQL password | +| `POSTGRES_PASSWORD` | local placeholder | PostgreSQL password | | `POSTGRES_DB` | `appdb` | PostgreSQL database name | +| `DB_WAIT_TIMEOUT` | `60` | Seconds the API entrypoint waits for DB connectivity | +| `GUNICORN_WORKERS` | `2` | Gunicorn worker count | +| `GUNICORN_THREADS` | `4` | Gunicorn threads per worker | +| `GUNICORN_TIMEOUT` | `60` | Gunicorn request timeout in seconds | | `PGADMIN_DEFAULT_EMAIL` | `admin@example.com` | pgAdmin login email | -| `PGADMIN_DEFAULT_PASSWORD` | `admin` | pgAdmin login password | +| `PGADMIN_DEFAULT_PASSWORD` | local placeholder | pgAdmin login password | --- @@ -86,14 +113,20 @@ curl -X POST http://localhost:5000/api/v1/items \ ```bash # Run tests locally +python -m venv .venv +source .venv/bin/activate pip install -r requirements.txt pytest --cov=app -v -# Lint +# Lint and format check ruff check . +ruff format --check . + +# Apply formatting intentionally ruff format . # Database migrations +export FLASK_APP='app:create_app()' flask db migrate -m "describe your change" flask db upgrade ``` @@ -102,6 +135,33 @@ flask db upgrade ## Project Structure +```text +. +├── app/ +│ ├── __init__.py # Flask application factory, health/readiness, error handlers +│ ├── config.py # Runtime and test configuration +│ ├── extensions.py # SQLAlchemy and Flask-Migrate extension instances +│ ├── models.py # Item SQLAlchemy model +│ ├── routes.py # REST API routes +│ └── schemas.py # Marshmallow request validation +├── migrations/ # Alembic migration environment and revisions +├── tests/ # Pytest API coverage +├── Dockerfile # Multi-stage production image +├── docker-compose.yml # API, Postgres, optional pgAdmin +├── entrypoint.sh # DB wait, migration, Gunicorn startup +├── requirements.txt # Runtime and dev dependencies +└── run.py # Local development entrypoint +``` + +--- + +## Operational Notes + +- `/health` is for container liveness. +- `/ready` is for load balancers and orchestrators because it checks database connectivity. +- The API performs migrations at startup inside Docker. In higher-control production environments, run migrations as a separate release job. +- PostgreSQL and pgAdmin are bound to localhost in Compose. Keep that default unless you intentionally need remote access. +- pgAdmin is optional and gated behind the `admin` Compose profile. --- From f81e817b5f5c79a2788aa94b122f15aa8aa9d9b2 Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:47 +0000 Subject: [PATCH 13/30] feat: add docker-flask-postgres-api ECC bundle (.claude/ecc-tools.json) --- .claude/ecc-tools.json | 304 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 .claude/ecc-tools.json diff --git a/.claude/ecc-tools.json b/.claude/ecc-tools.json new file mode 100644 index 0000000..52ad751 --- /dev/null +++ b/.claude/ecc-tools.json @@ -0,0 +1,304 @@ +{ + "version": "1.3", + "schemaVersion": "1.0", + "generatedBy": "ecc-tools", + "generatedAt": "2026-05-17T17:29:24.655Z", + "repo": "https://github.com/donny-devops/docker-flask-postgres-api", + "referenceSetReadiness": { + "score": 0, + "present": 0, + "total": 7, + "items": [ + { + "id": "deep-analyzer-corpus", + "label": "Deep analyzer corpus", + "status": "missing", + "evidence": [], + "recommendation": "Add analyzer fixture, golden, benchmark, or reference-set files that can catch analyzer regressions." + }, + { + "id": "rag-evaluator", + "label": "RAG/evaluator comparison", + "status": "missing", + "evidence": [], + "recommendation": "Add retrieval or evaluator reference-set comparison fixtures with expected ranking behavior." + }, + { + "id": "pr-salvage", + "label": "PR salvage/review corpus", + "status": "missing", + "evidence": [], + "recommendation": "Add stale-PR, review-thread, reopen-flow, or salvage reference cases for queue cleanup automation." + }, + { + "id": "discussion-triage", + "label": "Discussion triage corpus", + "status": "missing", + "evidence": [], + "recommendation": "Add public discussion triage fixtures, golden cases, or reference sets for informational, answered, and no-response classifications." + }, + { + "id": "harness-compatibility", + "label": "Harness compatibility", + "status": "missing", + "evidence": [], + "recommendation": "Add cross-harness, adapter-compliance, or harness-audit evidence for Claude, Codex, OpenCode, Zed, dmux, and agent surfaces." + }, + { + "id": "security-evidence", + "label": "Security evidence", + "status": "missing", + "evidence": [], + "recommendation": "Attach security evidence such as SBOMs, SARIF, audit reports, or AgentShield evidence packs." + }, + { + "id": "ci-failure-mode", + "label": "CI failure-mode evidence", + "status": "missing", + "evidence": [], + "recommendation": "Add captured CI failure logs, dry-run fixtures, or troubleshooting docs for common workflow failure modes." + } + ] + }, + "profiles": { + "requested": "full", + "recommended": "full", + "effective": "developer", + "requestedAlias": "full", + "recommendedAlias": "full", + "effectiveAlias": "developer" + }, + "requestedProfile": "full", + "profile": "developer", + "recommendedProfile": "full", + "effectiveProfile": "developer", + "tier": "free", + "requestedComponents": [ + "repo-baseline", + "workflow-automation", + "security-audits", + "research-tooling", + "team-rollout", + "governance-controls" + ], + "selectedComponents": [ + "repo-baseline", + "workflow-automation" + ], + "requestedAddComponents": [], + "requestedRemoveComponents": [], + "blockedRemovalComponents": [], + "tierFilteredComponents": [ + "security-audits", + "research-tooling", + "team-rollout", + "governance-controls" + ], + "requestedRootPackages": [ + "runtime-core", + "workflow-pack", + "agentshield-pack", + "research-pack", + "team-config-sync", + "enterprise-controls" + ], + "selectedRootPackages": [ + "runtime-core", + "workflow-pack" + ], + "requestedPackages": [ + "runtime-core", + "workflow-pack", + "agentshield-pack", + "research-pack", + "team-config-sync", + "enterprise-controls" + ], + "requestedAddPackages": [], + "requestedRemovePackages": [], + "selectedPackages": [ + "runtime-core", + "workflow-pack" + ], + "packages": [ + "runtime-core", + "workflow-pack" + ], + "blockedRemovalPackages": [], + "tierFilteredRootPackages": [ + "agentshield-pack", + "research-pack", + "team-config-sync", + "enterprise-controls" + ], + "tierFilteredPackages": [ + "agentshield-pack", + "research-pack", + "team-config-sync", + "enterprise-controls" + ], + "conflictingPackages": [], + "dependencyGraph": { + "runtime-core": [], + "workflow-pack": [ + "runtime-core" + ] + }, + "resolutionOrder": [ + "runtime-core", + "workflow-pack" + ], + "requestedModules": [ + "runtime-core", + "workflow-pack", + "agentshield-pack", + "research-pack", + "team-config-sync", + "enterprise-controls" + ], + "selectedModules": [ + "runtime-core", + "workflow-pack" + ], + "modules": [ + "runtime-core", + "workflow-pack" + ], + "managedFiles": [ + ".claude/skills/docker-flask-postgres-api/SKILL.md", + ".agents/skills/docker-flask-postgres-api/SKILL.md", + ".agents/skills/docker-flask-postgres-api/agents/openai.yaml", + ".claude/identity.json", + ".codex/config.toml", + ".codex/AGENTS.md", + ".codex/agents/explorer.toml", + ".codex/agents/reviewer.toml", + ".codex/agents/docs-researcher.toml", + ".claude/homunculus/instincts/inherited/docker-flask-postgres-api-instincts.yaml", + ".claude/commands/feature-development-api-endpoint.md", + ".claude/commands/documentation-update.md" + ], + "packageFiles": { + "runtime-core": [ + ".claude/skills/docker-flask-postgres-api/SKILL.md", + ".agents/skills/docker-flask-postgres-api/SKILL.md", + ".agents/skills/docker-flask-postgres-api/agents/openai.yaml", + ".claude/identity.json", + ".codex/config.toml", + ".codex/AGENTS.md", + ".codex/agents/explorer.toml", + ".codex/agents/reviewer.toml", + ".codex/agents/docs-researcher.toml", + ".claude/homunculus/instincts/inherited/docker-flask-postgres-api-instincts.yaml" + ], + "workflow-pack": [ + ".claude/commands/feature-development-api-endpoint.md", + ".claude/commands/documentation-update.md" + ] + }, + "moduleFiles": { + "runtime-core": [ + ".claude/skills/docker-flask-postgres-api/SKILL.md", + ".agents/skills/docker-flask-postgres-api/SKILL.md", + ".agents/skills/docker-flask-postgres-api/agents/openai.yaml", + ".claude/identity.json", + ".codex/config.toml", + ".codex/AGENTS.md", + ".codex/agents/explorer.toml", + ".codex/agents/reviewer.toml", + ".codex/agents/docs-researcher.toml", + ".claude/homunculus/instincts/inherited/docker-flask-postgres-api-instincts.yaml" + ], + "workflow-pack": [ + ".claude/commands/feature-development-api-endpoint.md", + ".claude/commands/documentation-update.md" + ] + }, + "files": [ + { + "moduleId": "runtime-core", + "path": ".claude/skills/docker-flask-postgres-api/SKILL.md", + "description": "Repository-specific Claude Code skill generated from git history." + }, + { + "moduleId": "runtime-core", + "path": ".agents/skills/docker-flask-postgres-api/SKILL.md", + "description": "Codex-facing copy of the generated repository skill." + }, + { + "moduleId": "runtime-core", + "path": ".agents/skills/docker-flask-postgres-api/agents/openai.yaml", + "description": "Codex skill metadata so the repo skill appears cleanly in the skill interface." + }, + { + "moduleId": "runtime-core", + "path": ".claude/identity.json", + "description": "Suggested identity.json baseline derived from repository conventions." + }, + { + "moduleId": "runtime-core", + "path": ".codex/config.toml", + "description": "Repo-local Codex MCP and multi-agent baseline aligned with ECC defaults." + }, + { + "moduleId": "runtime-core", + "path": ".codex/AGENTS.md", + "description": "Codex usage guide that points at the generated repo skill and workflow bundle." + }, + { + "moduleId": "runtime-core", + "path": ".codex/agents/explorer.toml", + "description": "Read-only explorer role config for Codex multi-agent work." + }, + { + "moduleId": "runtime-core", + "path": ".codex/agents/reviewer.toml", + "description": "Read-only reviewer role config focused on correctness and security." + }, + { + "moduleId": "runtime-core", + "path": ".codex/agents/docs-researcher.toml", + "description": "Read-only docs researcher role config for API verification." + }, + { + "moduleId": "runtime-core", + "path": ".claude/homunculus/instincts/inherited/docker-flask-postgres-api-instincts.yaml", + "description": "Continuous-learning instincts derived from repository patterns." + }, + { + "moduleId": "workflow-pack", + "path": ".claude/commands/feature-development-api-endpoint.md", + "description": "Workflow command scaffold for feature-development-api-endpoint." + }, + { + "moduleId": "workflow-pack", + "path": ".claude/commands/documentation-update.md", + "description": "Workflow command scaffold for documentation-update." + } + ], + "workflows": [ + { + "command": "feature-development-api-endpoint", + "path": ".claude/commands/feature-development-api-endpoint.md" + }, + { + "command": "documentation-update", + "path": ".claude/commands/documentation-update.md" + } + ], + "adapters": { + "claudeCode": { + "skillPath": ".claude/skills/docker-flask-postgres-api/SKILL.md", + "identityPath": ".claude/identity.json", + "commandPaths": [ + ".claude/commands/feature-development-api-endpoint.md", + ".claude/commands/documentation-update.md" + ] + }, + "codex": { + "configPath": ".codex/config.toml", + "agentsGuidePath": ".codex/AGENTS.md", + "skillPath": ".agents/skills/docker-flask-postgres-api/SKILL.md" + } + } +} \ No newline at end of file From c296e4691250ea14ef2cc737bc607e24cd681308 Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:48 +0000 Subject: [PATCH 14/30] feat: add docker-flask-postgres-api ECC bundle (.claude/skills/docker-flask-postgres-api/SKILL.md) --- .../skills/docker-flask-postgres-api/SKILL.md | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 .claude/skills/docker-flask-postgres-api/SKILL.md diff --git a/.claude/skills/docker-flask-postgres-api/SKILL.md b/.claude/skills/docker-flask-postgres-api/SKILL.md new file mode 100644 index 0000000..eb0b181 --- /dev/null +++ b/.claude/skills/docker-flask-postgres-api/SKILL.md @@ -0,0 +1,121 @@ +```markdown +# docker-flask-postgres-api Development Patterns + +> Auto-generated skill from repository analysis + +## Overview + +This skill teaches the core development patterns, coding conventions, and workflows used in the `docker-flask-postgres-api` repository. The repository is a Python-based API project, using Docker for deployment and PostgreSQL as the database backend. It emphasizes clear commit messages, modular code organization, and robust testing and documentation practices. + +## Coding Conventions + +- **File Naming:** + Use camelCase for file names. + _Example:_ + ``` + userRoutes.py + dbConnection.py + ``` + +- **Import Style:** + Use relative imports within the package. + _Example:_ + ```python + from .schemas import UserSchema + from .utils import validate_input + ``` + +- **Export Style:** + Use named exports (explicitly define what is exported). + _Example:_ + ```python + def create_user(...): + ... + + def delete_user(...): + ... + ``` + +- **Commit Messages:** + Follow [Conventional Commits](https://www.conventionalcommits.org/) with prefixes: `fix`, `docs`, `feat`, `test`. + _Example:_ + ``` + feat: add user registration endpoint with validation + fix: correct db connection string parsing + docs: update README with setup instructions + test: add tests for user login route + ``` + +## Workflows + +### Feature Development: API Endpoint +**Trigger:** When adding a new API endpoint with validation, error handling, and tests +**Command:** `/new-endpoint` + +1. **Implement endpoint logic** in `app/__init__.py` + _Example:_ + ```python + @app.route('/users', methods=['POST']) + def create_user(): + # Endpoint logic here + ``` +2. **Add or update validation** in `app/schemas.py` + _Example:_ + ```python + class UserSchema(Schema): + username = fields.Str(required=True) + email = fields.Email(required=True) + ``` +3. **Write or update tests** in `tests/test_routes.py` + _Example:_ + ```python + def test_create_user(client): + response = client.post('/users', json={...}) + assert response.status_code == 201 + ``` +4. **Update documentation** in `README.md` + - Document the new endpoint, request/response examples, and any new environment variables. + +--- + +### Documentation Update +**Trigger:** When documenting new features, configuration, or operational procedures +**Command:** `/update-docs` + +1. **Update environment variable documentation** in `.env.example` + - Add or update variables relevant to the new feature. +2. **Update main documentation** in `README.md` + - Describe new features, configuration steps, or architectural changes. + +--- + +## Testing Patterns + +- **Test File Naming:** + Test files follow the pattern `*.test.*` (e.g., `userRoutes.test.py`). + +- **Test Location:** + Tests are placed in the `tests/` directory, commonly in files like `tests/test_routes.py`. + +- **Test Structure:** + Each test function typically uses a client fixture to make HTTP requests to endpoints and asserts on the response. + + _Example:_ + ```python + def test_get_users(client): + response = client.get('/users') + assert response.status_code == 200 + assert isinstance(response.json, list) + ``` + +- **Testing Framework:** + The specific framework is not detected, but typical Python test frameworks (like `pytest`) are likely used. + +## Commands + +| Command | Purpose | +|----------------|-----------------------------------------------------------------| +| /new-endpoint | Start the workflow to add a new API endpoint with tests & docs | +| /update-docs | Start the workflow to update documentation and environment vars | + +``` \ No newline at end of file From 8cf9817ac68e4fd5b68e5521e78222a2350a9fb0 Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:49 +0000 Subject: [PATCH 15/30] feat: add docker-flask-postgres-api ECC bundle (.agents/skills/docker-flask-postgres-api/SKILL.md) --- .../skills/docker-flask-postgres-api/SKILL.md | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 .agents/skills/docker-flask-postgres-api/SKILL.md diff --git a/.agents/skills/docker-flask-postgres-api/SKILL.md b/.agents/skills/docker-flask-postgres-api/SKILL.md new file mode 100644 index 0000000..eb0b181 --- /dev/null +++ b/.agents/skills/docker-flask-postgres-api/SKILL.md @@ -0,0 +1,121 @@ +```markdown +# docker-flask-postgres-api Development Patterns + +> Auto-generated skill from repository analysis + +## Overview + +This skill teaches the core development patterns, coding conventions, and workflows used in the `docker-flask-postgres-api` repository. The repository is a Python-based API project, using Docker for deployment and PostgreSQL as the database backend. It emphasizes clear commit messages, modular code organization, and robust testing and documentation practices. + +## Coding Conventions + +- **File Naming:** + Use camelCase for file names. + _Example:_ + ``` + userRoutes.py + dbConnection.py + ``` + +- **Import Style:** + Use relative imports within the package. + _Example:_ + ```python + from .schemas import UserSchema + from .utils import validate_input + ``` + +- **Export Style:** + Use named exports (explicitly define what is exported). + _Example:_ + ```python + def create_user(...): + ... + + def delete_user(...): + ... + ``` + +- **Commit Messages:** + Follow [Conventional Commits](https://www.conventionalcommits.org/) with prefixes: `fix`, `docs`, `feat`, `test`. + _Example:_ + ``` + feat: add user registration endpoint with validation + fix: correct db connection string parsing + docs: update README with setup instructions + test: add tests for user login route + ``` + +## Workflows + +### Feature Development: API Endpoint +**Trigger:** When adding a new API endpoint with validation, error handling, and tests +**Command:** `/new-endpoint` + +1. **Implement endpoint logic** in `app/__init__.py` + _Example:_ + ```python + @app.route('/users', methods=['POST']) + def create_user(): + # Endpoint logic here + ``` +2. **Add or update validation** in `app/schemas.py` + _Example:_ + ```python + class UserSchema(Schema): + username = fields.Str(required=True) + email = fields.Email(required=True) + ``` +3. **Write or update tests** in `tests/test_routes.py` + _Example:_ + ```python + def test_create_user(client): + response = client.post('/users', json={...}) + assert response.status_code == 201 + ``` +4. **Update documentation** in `README.md` + - Document the new endpoint, request/response examples, and any new environment variables. + +--- + +### Documentation Update +**Trigger:** When documenting new features, configuration, or operational procedures +**Command:** `/update-docs` + +1. **Update environment variable documentation** in `.env.example` + - Add or update variables relevant to the new feature. +2. **Update main documentation** in `README.md` + - Describe new features, configuration steps, or architectural changes. + +--- + +## Testing Patterns + +- **Test File Naming:** + Test files follow the pattern `*.test.*` (e.g., `userRoutes.test.py`). + +- **Test Location:** + Tests are placed in the `tests/` directory, commonly in files like `tests/test_routes.py`. + +- **Test Structure:** + Each test function typically uses a client fixture to make HTTP requests to endpoints and asserts on the response. + + _Example:_ + ```python + def test_get_users(client): + response = client.get('/users') + assert response.status_code == 200 + assert isinstance(response.json, list) + ``` + +- **Testing Framework:** + The specific framework is not detected, but typical Python test frameworks (like `pytest`) are likely used. + +## Commands + +| Command | Purpose | +|----------------|-----------------------------------------------------------------| +| /new-endpoint | Start the workflow to add a new API endpoint with tests & docs | +| /update-docs | Start the workflow to update documentation and environment vars | + +``` \ No newline at end of file From d02fcc7c5e7a9f3464206c2511570312c2cc8b62 Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:50 +0000 Subject: [PATCH 16/30] feat: add docker-flask-postgres-api ECC bundle (.agents/skills/docker-flask-postgres-api/agents/openai.yaml) --- .agents/skills/docker-flask-postgres-api/agents/openai.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .agents/skills/docker-flask-postgres-api/agents/openai.yaml diff --git a/.agents/skills/docker-flask-postgres-api/agents/openai.yaml b/.agents/skills/docker-flask-postgres-api/agents/openai.yaml new file mode 100644 index 0000000..a80ab09 --- /dev/null +++ b/.agents/skills/docker-flask-postgres-api/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Docker Flask Postgres Api" + short_description: "Repo-specific patterns and workflows for docker-flask-postgres-api" + default_prompt: "Use the docker-flask-postgres-api repo skill to follow existing architecture, testing, and workflow conventions." +policy: + allow_implicit_invocation: true \ No newline at end of file From 34a54223404ac0a1d07752eefc4b198450fcbcbe Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:51 +0000 Subject: [PATCH 17/30] feat: add docker-flask-postgres-api ECC bundle (.claude/identity.json) --- .claude/identity.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .claude/identity.json diff --git a/.claude/identity.json b/.claude/identity.json new file mode 100644 index 0000000..0487339 --- /dev/null +++ b/.claude/identity.json @@ -0,0 +1,14 @@ +{ + "version": "2.0", + "technicalLevel": "technical", + "preferredStyle": { + "verbosity": "moderate", + "codeComments": true, + "explanations": true + }, + "domains": [ + "python" + ], + "suggestedBy": "ecc-tools-repo-analysis", + "createdAt": "2026-05-17T17:29:44.837Z" +} \ No newline at end of file From 1a095e5e68c8256725db610d9c1f9602c8e5f730 Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:52 +0000 Subject: [PATCH 18/30] feat: add docker-flask-postgres-api ECC bundle (.codex/config.toml) --- .codex/config.toml | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .codex/config.toml diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 0000000..bc1ee67 --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,48 @@ +#:schema https://developers.openai.com/codex/config-schema.json + +# ECC Tools generated Codex baseline +approval_policy = "on-request" +sandbox_mode = "workspace-write" +web_search = "live" + +[mcp_servers.github] +command = "npx" +args = ["-y", "@modelcontextprotocol/server-github"] + +[mcp_servers.context7] +command = "npx" +args = ["-y", "@upstash/context7-mcp@latest"] + +[mcp_servers.exa] +url = "https://mcp.exa.ai/mcp" + +[mcp_servers.memory] +command = "npx" +args = ["-y", "@modelcontextprotocol/server-memory"] + +[mcp_servers.playwright] +command = "npx" +args = ["-y", "@playwright/mcp@latest", "--extension"] + +[mcp_servers.sequential-thinking] +command = "npx" +args = ["-y", "@modelcontextprotocol/server-sequential-thinking"] + +[features] +multi_agent = true + +[agents] +max_threads = 6 +max_depth = 1 + +[agents.explorer] +description = "Read-only codebase explorer for gathering evidence before changes are proposed." +config_file = "agents/explorer.toml" + +[agents.reviewer] +description = "PR reviewer focused on correctness, security, and missing tests." +config_file = "agents/reviewer.toml" + +[agents.docs_researcher] +description = "Documentation specialist that verifies APIs, framework behavior, and release notes." +config_file = "agents/docs-researcher.toml" \ No newline at end of file From 5cf6b7532decf806e1bc5242453c058d051eb73b Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:53 +0000 Subject: [PATCH 19/30] feat: add docker-flask-postgres-api ECC bundle (.codex/AGENTS.md) --- .codex/AGENTS.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .codex/AGENTS.md diff --git a/.codex/AGENTS.md b/.codex/AGENTS.md new file mode 100644 index 0000000..2429b28 --- /dev/null +++ b/.codex/AGENTS.md @@ -0,0 +1,27 @@ +# ECC for Codex CLI + +This supplements the root `AGENTS.md` with a repo-local ECC baseline. + +## Repo Skill + +- Repo-generated Codex skill: `.agents/skills/docker-flask-postgres-api/SKILL.md` +- Claude-facing companion skill: `.claude/skills/docker-flask-postgres-api/SKILL.md` +- Keep user-specific credentials and private MCPs in `~/.codex/config.toml`, not in this repo. + +## MCP Baseline + +Treat `.codex/config.toml` as the default ECC-safe baseline for work in this repository. +The generated baseline enables GitHub, Context7, Exa, Memory, Playwright, and Sequential Thinking. + +## Multi-Agent Support + +- Explorer: read-only evidence gathering +- Reviewer: correctness, security, and regression review +- Docs researcher: API and release-note verification + +## Workflow Files + +- `.claude/commands/feature-development-api-endpoint.md` +- `.claude/commands/documentation-update.md` + +Use these workflow files as reusable task scaffolds when the detected repository workflows recur. \ No newline at end of file From 3c29728d7fcf8df7646391ef4ca566b8fabbc620 Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:54 +0000 Subject: [PATCH 20/30] feat: add docker-flask-postgres-api ECC bundle (.codex/agents/explorer.toml) --- .codex/agents/explorer.toml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .codex/agents/explorer.toml diff --git a/.codex/agents/explorer.toml b/.codex/agents/explorer.toml new file mode 100644 index 0000000..732df7a --- /dev/null +++ b/.codex/agents/explorer.toml @@ -0,0 +1,9 @@ +model = "gpt-5.4" +model_reasoning_effort = "medium" +sandbox_mode = "read-only" + +developer_instructions = """ +Stay in exploration mode. +Trace the real execution path, cite files and symbols, and avoid proposing fixes unless the parent agent asks for them. +Prefer targeted search and file reads over broad scans. +""" \ No newline at end of file From d1ba51218f1d52832c3eefac2e44caed78d45cc7 Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:55 +0000 Subject: [PATCH 21/30] feat: add docker-flask-postgres-api ECC bundle (.codex/agents/reviewer.toml) --- .codex/agents/reviewer.toml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .codex/agents/reviewer.toml diff --git a/.codex/agents/reviewer.toml b/.codex/agents/reviewer.toml new file mode 100644 index 0000000..b13ed9c --- /dev/null +++ b/.codex/agents/reviewer.toml @@ -0,0 +1,9 @@ +model = "gpt-5.4" +model_reasoning_effort = "high" +sandbox_mode = "read-only" + +developer_instructions = """ +Review like an owner. +Prioritize correctness, security, behavioral regressions, and missing tests. +Lead with concrete findings and avoid style-only feedback unless it hides a real bug. +""" \ No newline at end of file From 73cf48ac154458cc5d5afca81c041966eee50d20 Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:56 +0000 Subject: [PATCH 22/30] feat: add docker-flask-postgres-api ECC bundle (.codex/agents/docs-researcher.toml) --- .codex/agents/docs-researcher.toml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .codex/agents/docs-researcher.toml diff --git a/.codex/agents/docs-researcher.toml b/.codex/agents/docs-researcher.toml new file mode 100644 index 0000000..0daae57 --- /dev/null +++ b/.codex/agents/docs-researcher.toml @@ -0,0 +1,9 @@ +model = "gpt-5.4" +model_reasoning_effort = "medium" +sandbox_mode = "read-only" + +developer_instructions = """ +Verify APIs, framework behavior, and release-note claims against primary documentation before changes land. +Cite the exact docs or file paths that support each claim. +Do not invent undocumented behavior. +""" \ No newline at end of file From 0223eb2abba3435c20203fba35cac790c43782b1 Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:57 +0000 Subject: [PATCH 23/30] feat: add docker-flask-postgres-api ECC bundle (.claude/homunculus/instincts/inherited/docker-flask-postgres-api-instincts.yaml) --- .../docker-flask-postgres-api-instincts.yaml | 447 ++++++++++++++++++ 1 file changed, 447 insertions(+) create mode 100644 .claude/homunculus/instincts/inherited/docker-flask-postgres-api-instincts.yaml diff --git a/.claude/homunculus/instincts/inherited/docker-flask-postgres-api-instincts.yaml b/.claude/homunculus/instincts/inherited/docker-flask-postgres-api-instincts.yaml new file mode 100644 index 0000000..d9916d0 --- /dev/null +++ b/.claude/homunculus/instincts/inherited/docker-flask-postgres-api-instincts.yaml @@ -0,0 +1,447 @@ +# Instincts generated from https://github.com/donny-devops/docker-flask-postgres-api +# Generated: 2026-05-17T17:29:44.837Z +# Version: 2.0 +# NOTE: This file supplements (does not replace) any existing curated instincts. +# High-confidence manually curated instincts should be preserved alongside these. + +--- +id: docker-flask-postgres-api-commit-conventional +trigger: "when writing a commit message" +confidence: 0.85 +domain: git +source: repo-analysis +source_repo: https://github.com/donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Commit Conventional + +## Action + +Use conventional commit format with prefixes: fix, docs, feat, test + +## Evidence + +- 8 commits analyzed +- Detected conventional commit pattern +- Examples: fix(api): make Marshmallow validators compatible with newer callbacks, feat(api): add readiness endpoint and structured error responses + +--- +id: docker-flask-postgres-api-commit-length +trigger: "when writing a commit message" +confidence: 0.6 +domain: git +source: repo-analysis +source_repo: https://github.com/donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Commit Length + +## Action + +Write moderate-length commit messages (~57 characters) + +## Evidence + +- Average commit message length: 57 chars +- Based on 8 commits + +--- +id: docker-flask-postgres-api-naming-files +trigger: "when creating a new file" +confidence: 0.8 +domain: code-style +source: repo-analysis +source_repo: https://github.com/donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Naming Files + +## Action + +Use camelCase naming convention + +## Evidence + +- Analyzed file naming patterns in repository +- Dominant pattern: camelCase + +--- +id: docker-flask-postgres-api-import-relative +trigger: "when importing modules" +confidence: 0.75 +domain: code-style +source: repo-analysis +source_repo: https://github.com/donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Import Relative + +## Action + +Use relative imports for project files + +## Evidence + +- Import analysis shows relative import pattern +- Example: import { x } from '../lib/x' + +--- +id: docker-flask-postgres-api-export-style +trigger: "when exporting from a module" +confidence: 0.7 +domain: code-style +source: repo-analysis +source_repo: https://github.com/donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Export Style + +## Action + +Prefer named exports + +## Evidence + +- Export pattern analysis +- Dominant style: named + +--- +id: docker-flask-postgres-api-test-separate +trigger: "when writing tests" +confidence: 0.8 +domain: testing +source: repo-analysis +source_repo: https://github.com/donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Test Separate + +## Action + +Place tests in the tests/ or __tests__/ directory, mirroring src structure + +## Evidence + +- Separate test directory pattern detected +- Tests live in dedicated test folders + +--- +id: docker-flask-postgres-api-workflow-feature-development-api-endpoint +trigger: "when doing feature development api endpoint" +confidence: 0.6 +domain: workflow +source: repo-analysis +source_repo: https://github.com/donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Workflow Feature Development Api Endpoint + +## Action + +Follow the feature-development-api-endpoint workflow: +1. Implement endpoint logic in app/__init__.py +2. Add or update validation in app/schemas.py +3. Write or update tests in tests/test_routes.py +4. Update documentation in README.md + +## Evidence + +- Workflow detected from commit patterns +- Frequency: ~2x per month +- Files: app/__init__.py, app/schemas.py, tests/test_routes.py + +--- +id: docker-flask-postgres-api-workflow-documentation-update +trigger: "when doing documentation update" +confidence: 0.6 +domain: workflow +source: repo-analysis +source_repo: https://github.com/donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Workflow Documentation Update + +## Action + +Follow the documentation-update workflow: +1. Update environment variable documentation in .env.example +2. Update main documentation in README.md + +## Evidence + +- Workflow detected from commit patterns +- Frequency: ~2x per month +- Files: .env.example, README.md + +--- +id: docker-flask-postgres-api-instinct-filename-camelcase +trigger: "When creating or renaming a source file in the repository." +confidence: 0.8 +domain: code-style +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Filename Camelcase + +## Action + +Name the file using camelCase (e.g., myFile.py). + +## Evidence + +- Pattern in codeStyle.namingConventions.files + +--- +id: docker-flask-postgres-api-instinct-function-camelcase +trigger: "When defining a new function." +confidence: 0.8 +domain: code-style +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Function Camelcase + +## Action + +Name the function using camelCase (e.g., processData). + +## Evidence + +- Pattern in codeStyle.namingConventions.functions + +--- +id: docker-flask-postgres-api-instinct-class-pascalcase +trigger: "When defining a new class." +confidence: 0.8 +domain: code-style +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Class Pascalcase + +## Action + +Name the class using PascalCase (e.g., DataProcessor). + +## Evidence + +- Pattern in codeStyle.namingConventions.classes + +--- +id: docker-flask-postgres-api-instinct-constant-screaming-snake +trigger: "When defining a constant." +confidence: 0.8 +domain: code-style +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Constant Screaming Snake + +## Action + +Name the constant using SCREAMING_SNAKE_CASE (e.g., MAX_SIZE). + +## Evidence + +- Pattern in codeStyle.namingConventions.constants + +--- +id: docker-flask-postgres-api-instinct-import-relative +trigger: "When importing modules within the package." +confidence: 0.8 +domain: code-style +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Import Relative + +## Action + +Use relative imports. + +## Evidence + +- Pattern in codeStyle.importStyle + +--- +id: docker-flask-postgres-api-instinct-export-named +trigger: "When exporting functions, classes, or variables." +confidence: 0.8 +domain: code-style +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Export Named + +## Action + +Use named exports. + +## Evidence + +- Pattern in codeStyle.exportStyle + +--- +id: docker-flask-postgres-api-instinct-test-location-separate +trigger: "When adding or updating tests." +confidence: 0.9 +domain: testing +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Test Location Separate + +## Action + +Place test files in the separate 'tests' directory. + +## Evidence + +- Pattern in architecture.folderStructure.testLocation +- tests/test_routes.py + +--- +id: docker-flask-postgres-api-instinct-test-file-naming +trigger: "When naming a test file." +confidence: 0.85 +domain: testing +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Test File Naming + +## Action + +Use the 'test_' prefix (e.g., test_routes.py). + +## Evidence + +- tests/test_routes.py + +--- +id: docker-flask-postgres-api-instinct-test-update-on-feature +trigger: "When implementing a new API endpoint or feature." +confidence: 0.9 +domain: testing +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Test Update On Feature + +## Action + +Add or update corresponding tests in the 'tests' directory. + +## Evidence + +- Workflow feature-development-api-endpoint +- Commits like 'test(api): cover readiness validation and JSON error behavior' + +--- +id: docker-flask-postgres-api-instinct-commit-conventional-format +trigger: "When writing a commit message." +confidence: 0.9 +domain: git +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Commit Conventional Format + +## Action + +Use the conventional commit format: (): . + +## Evidence + +- Pattern in commits.type and prefixes +- Examples in commits.examples + +--- +id: docker-flask-postgres-api-instinct-commit-prefixes +trigger: "When categorizing a commit." +confidence: 0.9 +domain: git +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Commit Prefixes + +## Action + +Use one of the prefixes: fix, feat, docs, test. + +## Evidence + +- Pattern in commits.prefixes +- Examples in commits.examples + +--- +id: docker-flask-postgres-api-instinct-commit-length +trigger: "When writing a commit message." +confidence: 0.7 +domain: git +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Commit Length + +## Action + +Keep the commit message concise, around 57 characters on average. + +## Evidence + +- Pattern in commits.averageLength + +--- +id: docker-flask-postgres-api-instinct-workflow-new-endpoint +trigger: "When someone wants to add a new API endpoint with proper validation, error handling, and test coverage." +confidence: 0.9 +domain: workflow +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Workflow New Endpoint + +## Action + +Follow the steps: implement logic in app/__init__.py, update validation in app/schemas.py, write/update tests in tests/test_routes.py, update documentation in README.md. + +## Evidence + +- Workflow feature-development-api-endpoint +- Commit sequence and files involved + +--- +id: docker-flask-postgres-api-instinct-workflow-update-docs +trigger: "When someone wants to document new features, configuration, or operational procedures." +confidence: 0.85 +domain: workflow +source: repo-analysis +source_repo: donny-devops/docker-flask-postgres-api +--- + +# Docker Flask Postgres Api Instinct Workflow Update Docs + +## Action + +Update environment variable documentation in .env.example and main documentation in README.md. + +## Evidence + +- Workflow documentation-update +- Commit sequence and files involved + From 5ee80687bd81d62afa4d6023a5b820aff564cbb5 Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:58 +0000 Subject: [PATCH 24/30] feat: add docker-flask-postgres-api ECC bundle (.claude/commands/feature-development-api-endpoint.md) --- .../feature-development-api-endpoint.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .claude/commands/feature-development-api-endpoint.md diff --git a/.claude/commands/feature-development-api-endpoint.md b/.claude/commands/feature-development-api-endpoint.md new file mode 100644 index 0000000..f3b50c1 --- /dev/null +++ b/.claude/commands/feature-development-api-endpoint.md @@ -0,0 +1,39 @@ +--- +name: feature-development-api-endpoint +description: Workflow command scaffold for feature-development-api-endpoint in docker-flask-postgres-api. +allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"] +--- + +# /feature-development-api-endpoint + +Use this workflow when working on **feature-development-api-endpoint** in `docker-flask-postgres-api`. + +## Goal + +Implements a new API endpoint, including code, tests, and documentation. + +## Common Files + +- `app/__init__.py` +- `app/schemas.py` +- `tests/test_routes.py` +- `README.md` + +## Suggested Sequence + +1. Understand the current state and failure mode before editing. +2. Make the smallest coherent change that satisfies the workflow goal. +3. Run the most relevant verification for touched files. +4. Summarize what changed and what still needs review. + +## Typical Commit Signals + +- Implement endpoint logic in app/__init__.py +- Add or update validation in app/schemas.py +- Write or update tests in tests/test_routes.py +- Update documentation in README.md + +## Notes + +- Treat this as a scaffold, not a hard-coded script. +- Update the command if the workflow evolves materially. \ No newline at end of file From 08cab313f29889b565447328184893f13fec02be Mon Sep 17 00:00:00 2001 From: "ecc-tools[bot]" <257055122+ecc-tools[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 17:29:59 +0000 Subject: [PATCH 25/30] feat: add docker-flask-postgres-api ECC bundle (.claude/commands/documentation-update.md) --- .claude/commands/documentation-update.md | 35 ++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .claude/commands/documentation-update.md diff --git a/.claude/commands/documentation-update.md b/.claude/commands/documentation-update.md new file mode 100644 index 0000000..5c40043 --- /dev/null +++ b/.claude/commands/documentation-update.md @@ -0,0 +1,35 @@ +--- +name: documentation-update +description: Workflow command scaffold for documentation-update in docker-flask-postgres-api. +allowed_tools: ["Bash", "Read", "Write", "Grep", "Glob"] +--- + +# /documentation-update + +Use this workflow when working on **documentation-update** in `docker-flask-postgres-api`. + +## Goal + +Updates documentation files to reflect new features, environment variables, or architectural changes. + +## Common Files + +- `.env.example` +- `README.md` + +## Suggested Sequence + +1. Understand the current state and failure mode before editing. +2. Make the smallest coherent change that satisfies the workflow goal. +3. Run the most relevant verification for touched files. +4. Summarize what changed and what still needs review. + +## Typical Commit Signals + +- Update environment variable documentation in .env.example +- Update main documentation in README.md + +## Notes + +- Treat this as a scaffold, not a hard-coded script. +- Update the command if the workflow evolves materially. \ No newline at end of file From 66c2a6c605d64172a58090e495c1748f55f54967 Mon Sep 17 00:00:00 2001 From: Donny <271941740+donny-devops@users.noreply.github.com> Date: Sun, 17 May 2026 15:11:58 -0400 Subject: [PATCH 26/30] security: add repository hygiene workflow --- .github/workflows/security-hygiene.yml | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/security-hygiene.yml diff --git a/.github/workflows/security-hygiene.yml b/.github/workflows/security-hygiene.yml new file mode 100644 index 0000000..0f873a1 --- /dev/null +++ b/.github/workflows/security-hygiene.yml @@ -0,0 +1,35 @@ +name: Security Hygiene + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +jobs: + hygiene: + name: Secret hygiene + runs-on: ubuntu-latest + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Block obvious private keys and tokens + shell: bash + run: | + SECRET_PATTERN='BEGIN (RSA |OPENSSH |EC |DSA )?PRIVATE KEY|ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{20,}|sk-[A-Za-z0-9]{20,}' + if grep -RInE "$SECRET_PATTERN" . \ + --exclude-dir=.git \ + --exclude=.github/workflows/security-hygiene.yml; then + echo "Potential secret material found. Remove it and rotate the credential." + exit 1 + fi From babb9893c5715a074ecefba47bf58ab3c78ad144 Mon Sep 17 00:00:00 2001 From: Donny <271941740+donny-devops@users.noreply.github.com> Date: Wed, 20 May 2026 19:37:46 -0400 Subject: [PATCH 27/30] Add AgentOps fleet quality gate --- .github/workflows/agentops-fleet.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/agentops-fleet.yml diff --git a/.github/workflows/agentops-fleet.yml b/.github/workflows/agentops-fleet.yml new file mode 100644 index 0000000..3088da4 --- /dev/null +++ b/.github/workflows/agentops-fleet.yml @@ -0,0 +1,22 @@ +name: AgentOps Fleet Gate + +on: + push: + pull_request: + workflow_dispatch: + schedule: + - cron: '31 8 * * 1' + +permissions: + contents: read + actions: read + security-events: write + pull-requests: read + +jobs: + agentops: + uses: donny-devops/github-actions-templates/.github/workflows/reusable-agentops.yml@main + with: + python-version: '3.12' + node-version: '22' + run-security-audit: true From d2505af7936e8ac64fda3829ebd3f65f86d24f31 Mon Sep 17 00:00:00 2001 From: Adonis Jimenez <271941740+donny-devops@users.noreply.github.com> Date: Fri, 29 May 2026 04:58:33 -0400 Subject: [PATCH 28/30] ci: enable step-security/harden-runner on all jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the step-security/harden-runner action as the first step of each job in ci.yml (lint, test, docker) with egress-policy: audit. This enables runtime network monitoring on GitHub-hosted runners without blocking any current outbound calls — drops will be logged for review, and the policy can be flipped to `block` once the baseline is known. Resolves the StepSecurity finding from #23. Action pinned to SHA fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 (v2.16.0) per repository policy and the issue's auto-generated remediation. --- .github/workflows/ci.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7be5d57..b4565b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,11 @@ jobs: permissions: contents: write steps: + - name: Harden the runner (audit outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - uses: actions/checkout@v4 with: ref: ${{ github.head_ref }} @@ -59,10 +64,15 @@ jobs: - 5432:5432 env: - DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb + DATABASE_URL: postgresql://testuser:***@localhost:5432/testdb SECRET_KEY: test-secret steps: + - name: Harden the runner (audit outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: @@ -100,6 +110,11 @@ jobs: packages: write steps: + - name: Harden the runner (audit outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - uses: actions/checkout@v4 - name: Set up Docker Buildx From 5a96ca6c36f23ba91a0517c7552434dfca59764d Mon Sep 17 00:00:00 2001 From: Adonis Jimenez <271941740+donny-devops@users.noreply.github.com> Date: Mon, 8 Jun 2026 06:58:48 -0400 Subject: [PATCH 29/30] fix(ci): add DATABASE_URL to Run migrations step env to fix auth failure --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4565b3..6129219 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,10 +89,12 @@ jobs: flask db upgrade env: FLASK_APP: "app:create_app()" + DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb - name: Run pytest run: | pytest --cov=app --cov-report=xml --cov-fail-under=85 -v + - name: Upload coverage uses: actions/upload-artifact@v4 From d10f420d311ac65f7b5c0b1239c5d205ee10329c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2026 11:50:58 +0000 Subject: [PATCH 30/30] chore(deps): bump actions/checkout from 4 to 7 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 64eb9a5..5325c25 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: name: Lint (ruff) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - uses: actions/setup-python@v5 with: @@ -54,7 +54,7 @@ jobs: DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb SECRET_KEY: test-secret steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - uses: actions/setup-python@v5 with: @@ -91,7 +91,7 @@ jobs: security-events: write if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3