Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c5b34ae
Restore styled and reachable invoice exception review workflow
jadenfix Oct 13, 2026
dbd402b
Align frontend formatting and coverage with actual Vitest tooling
jadenfix Oct 13, 2026
ec3a170
fix: preserve invoice calendar dates across client timezones
jadenfix Oct 13, 2026
30411ff
Implement actual PostgreSQL RabbitMQ PDF extraction, persisted auth a…
jadenfix Oct 13, 2026
b017fa4
Wait for RabbitMQ application and AMQP listener with bounded startup …
jadenfix Oct 13, 2026
6a5f2c0
Complete authenticated invoice frontend signup upload source document…
jadenfix Oct 13, 2026
b5865bd
Format authenticated API regression tests
jadenfix Oct 13, 2026
17a7650
Probe actual AMQP readiness, preserve robust recovery and parse sorta…
jadenfix Oct 13, 2026
b8cf42f
Verify exception review state transitions and enforce unchanged 95 pe…
jadenfix Oct 13, 2026
31ab6df
Repair legacy backend lint, typed execution and scanner setup
jadenfix Oct 13, 2026
ae9eed0
Retain shutdown cleanup after failed production startup
jadenfix Oct 13, 2026
f00b905
Use typed SQLAlchemy instance attributes without changing database nu…
jadenfix Oct 13, 2026
8ee5778
Preserve native PostgreSQL UUID and support pinned SQLite regression …
jadenfix Oct 13, 2026
aba8a2e
Install database and broker testcontainer dependencies used by legacy…
jadenfix Oct 13, 2026
0dde5df
Install timeout plugin required by legacy integration test command
jadenfix Oct 13, 2026
83207de
Exercise legacy HTTP and dependency failure contracts at unchanged co…
jadenfix Oct 13, 2026
71b82c6
Exercise production review routes with real database and broker integ…
jadenfix Oct 13, 2026
42c0438
Complete reversible invoice migrations and execute actual browser pip…
jadenfix Oct 13, 2026
1e354f3
Reconcile current main settings startup with durable invoice workflow…
jadenfix Oct 13, 2026
6f28ca2
Restore Terraform signed provider validation with maintained CLI and …
jadenfix Oct 13, 2026
7c6576f
Fix pipeline dependency security findings and declare real queue test…
jadenfix Oct 13, 2026
5bd2654
Repair auth security and health contracts and match ORM test readiness
jadenfix Oct 13, 2026
ccc7014
Declare validated authentication and coverage dependencies
jadenfix Oct 13, 2026
74eb988
Install tools required by ordinary auth and match CI checks
jadenfix Oct 13, 2026
6a028b2
Verify real matching database and broker roundtrip with safe decimal …
jadenfix Oct 13, 2026
25fc3fa
Verify original auth HTTP and PostgreSQL persistence without mocked p…
jadenfix Oct 13, 2026
28be876
Use validated Pydantic version for strict matching type checks and ru…
jadenfix Oct 13, 2026
b25c3c9
Probe required matching queues without destructive readiness deletion
jadenfix Oct 13, 2026
1bce4e7
Harden Terraform network, encryption and audit defaults with scoped l…
jadenfix Oct 13, 2026
81edc2b
Execute ordinary browser gate through the real current invoice workflow
jadenfix Oct 13, 2026
418fe39
Retain constructor checking for both Pydantic settings and matching DTOs
jadenfix Oct 13, 2026
152e61e
Complete validated production, TLS ingress, rotated database credenti…
jadenfix Oct 13, 2026
f7b304e
Correct WAF key output contract and explicit S3 lifecycle filters
jadenfix Oct 13, 2026
af76d2b
Route legacy ingest and extract entrypoints through the real pipeline
jadenfix Oct 13, 2026
7914f31
Preserve documented ingest and extract CLI port defaults
jadenfix Oct 13, 2026
7b884c4
Reject silently ignored malformed secondary invoice totals
jadenfix Oct 13, 2026
c7d6d64
Install genuine PDF contract test dependency in backend regression gate
jadenfix Oct 13, 2026
466c372
Consolidate legacy demo on real text-PDF pipeline with verified local…
jadenfix Oct 13, 2026
d5a136e
Test explicit local S3 configuration and storage failure propagation
jadenfix Oct 13, 2026
4edf894
Verify rejected terminal publications preserve real PostgreSQL outbox…
jadenfix Oct 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
294 changes: 45 additions & 249 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: InvoiceFlow CI/CD Pipeline

on:
workflow_dispatch:
push:
branches: [ main, develop ]
pull_request:
Expand All @@ -17,22 +18,24 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.0
terraform_version: 1.15.9

- name: Terraform Format Check
run: |
find infra/ -name "*.tf" -exec terraform fmt -check=true {} \;
terraform fmt -check -recursive infra/

- name: Terraform Validation
run: |
for env in dev staging prod; do
echo "Validating $env environment..."
cd infra/environments/$env
terraform init -backend=false
terraform init -backend=false -input=false
terraform validate
cd ../../..
done
Expand All @@ -47,11 +50,18 @@ jobs:
with:
directory: infra/
framework: terraform
skip_download: true
skip_results_upload: true
download_external_modules: false

# Security scanning
security-scan:
name: Security Scan
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
actions: read
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -65,7 +75,7 @@ jobs:
output: 'trivy-results.sarif'

- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v2
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'

Expand All @@ -76,87 +86,27 @@ jobs:
needs: [auth-service-tests, match-service-tests]
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Set up Python
uses: actions/setup-python@v4
- uses: actions/checkout@v4
with:
python-version: '3.11'

- name: Install e2e test dependencies
run: |
cd services/e2e
pip install -r requirements.txt

- name: Build service images
run: |
# Build images for all services
if [ -d "services/ingest" ]; then
docker build -t invoiceflow/ingest:e2e services/ingest/
fi
if [ -d "services/extract" ]; then
docker build -t invoiceflow/extract:e2e services/extract/
fi
if [ -d "services/match" ]; then
docker build -t invoiceflow/match:e2e services/match/
fi

- name: Start services with docker-compose
run: |
docker-compose -f docker-compose.e2e.yml up -d

# Wait for services to be healthy
timeout=120
elapsed=0

while [ $elapsed -lt $timeout ]; do
if docker-compose -f docker-compose.e2e.yml ps | grep -q "healthy"; then
ingest_health=$(docker inspect invoiceflow-ingest-e2e --format='{{.State.Health.Status}}' 2>/dev/null || echo "unknown")
extract_health=$(docker inspect invoiceflow-extract-e2e --format='{{.State.Health.Status}}' 2>/dev/null || echo "unknown")
match_health=$(docker inspect invoiceflow-match-e2e --format='{{.State.Health.Status}}' 2>/dev/null || echo "unknown")

if [ "$ingest_health" = "healthy" ] && [ "$extract_health" = "healthy" ] && [ "$match_health" = "healthy" ]; then
echo "All services are healthy"
break
fi
fi

echo "Waiting for services to become healthy... ($elapsed/$timeout)"
sleep 10
elapsed=$((elapsed + 10))
done

if [ $elapsed -ge $timeout ]; then
echo "Services failed to become healthy"
docker-compose -f docker-compose.e2e.yml ps
docker-compose -f docker-compose.e2e.yml logs
exit 1
fi

- name: Run end-to-end tests
timeout-minutes: 5
run: |
cd services/e2e
python -m pytest test_full_pipeline.py -v --timeout=300

- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-python@v5
with:
name: e2e-test-results
path: |
services/e2e/htmlcov/
services/e2e/coverage.xml

- name: Cleanup services
if: always()
run: |
docker-compose -f docker-compose.e2e.yml down --remove-orphans
docker-compose -f docker-compose.e2e.yml rm -f
python-version: '3.11'
- run: pip install -r services/pipeline/requirements.txt
- run: python -m pytest tests/pipeline tests/legacy/test_document_contract.py -q
- run: docker compose -f docker-compose.e2e.yml up -d postgres rabbitmq localstack --wait --wait-timeout 180
- run: python scripts/check_legacy_storage.py --bootstrap
- run: docker compose -f docker-compose.e2e.yml up -d --build --wait --wait-timeout 240
- run: python -m pytest services/e2e/test_full_pipeline.py -v
- if: always()
run: docker compose -f docker-compose.e2e.yml logs --no-color | tee legacy-provider-e2e.log
- if: always()
uses: actions/upload-artifact@v4
with:
name: legacy-provider-e2e
path: legacy-provider-e2e.log
- if: always()
run: docker compose -f docker-compose.e2e.yml down --remove-orphans

# Auth Service Testing
auth-service-tests:
Expand Down Expand Up @@ -199,6 +149,7 @@ jobs:
cd services/auth
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install flake8 mypy pytest-cov

- name: Lint Python code
run: |
Expand All @@ -220,6 +171,15 @@ jobs:
cd services/auth
python -m pytest tests/ -v --cov=app --cov-report=xml --cov-report=term-missing

- name: Real PostgreSQL auth HTTP roundtrip
env:
DATABASE_URL: postgresql://testuser:testpassword@localhost:5432/testdb
JWT_SECRET: test-secret-key-that-is-long-enough-for-testing
ENVIRONMENT: testing
run: |
cd services/auth
python scripts/check_native_auth.py

- name: Upload auth service coverage
uses: codecov/codecov-action@v3
with:
Expand Down Expand Up @@ -277,6 +237,7 @@ jobs:
run: |
cd services/match
pip install -r requirements.txt
pip install flake8 black isort "mypy==1.11.2"

- name: Lint match service code
run: |
Expand Down Expand Up @@ -355,175 +316,10 @@ jobs:
# E2E Testing
e2e-tests:
name: End-to-End Tests
runs-on: ubuntu-latest
needs: [auth-service-tests, frontend-tests]
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: testpassword
POSTGRES_USER: testuser
POSTGRES_DB: testdb
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install Python dependencies
run: |
cd services/auth
pip install -r requirements.txt

- name: Install frontend dependencies
run: |
cd frontend
npm ci

- name: Install Playwright
run: |
cd frontend
npx playwright install --with-deps

- name: Start auth service
env:
DATABASE_URL: postgresql://testuser:testpassword@localhost:5432/testdb
JWT_SECRET: test-secret-key-that-is-long-enough-for-testing
ENVIRONMENT: testing
run: |
cd services/auth
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 &
sleep 10

- name: Start frontend
env:
VITE_AUTH_SERVICE_URL: http://localhost:8000
run: |
cd frontend
npm run build
npm run preview --port 3000 &
sleep 10

- name: Run E2E tests
run: |
cd frontend
npm run test:e2e

- name: Upload E2E test results
uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: frontend/playwright-report/
uses: ./.github/workflows/durable-pipeline.yml

# End-to-End Pipeline Tests
e2e-pipeline:
name: End-to-End Pipeline Tests
runs-on: ubuntu-latest
needs: [auth-service-tests, match-service-tests]
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install e2e test dependencies
run: |
cd services/e2e
pip install -r requirements.txt

- name: Build service images
run: |
# Build images for all services
if [ -d "services/ingest" ]; then
docker build -t invoiceflow/ingest:e2e services/ingest/
fi
if [ -d "services/extract" ]; then
docker build -t invoiceflow/extract:e2e services/extract/
fi
if [ -d "services/match" ]; then
docker build -t invoiceflow/match:e2e services/match/
fi

- name: Start services with docker-compose
run: |
docker-compose -f docker-compose.e2e.yml up -d

# Wait for services to be healthy
timeout=120
elapsed=0

while [ $elapsed -lt $timeout ]; do
if docker-compose -f docker-compose.e2e.yml ps | grep -q "healthy"; then
ingest_health=$(docker inspect invoiceflow-ingest-e2e --format='{{.State.Health.Status}}' 2>/dev/null || echo "unknown")
extract_health=$(docker inspect invoiceflow-extract-e2e --format='{{.State.Health.Status}}' 2>/dev/null || echo "unknown")
match_health=$(docker inspect invoiceflow-match-e2e --format='{{.State.Health.Status}}' 2>/dev/null || echo "unknown")

if [ "$ingest_health" = "healthy" ] && [ "$extract_health" = "healthy" ] && [ "$match_health" = "healthy" ]; then
echo "All services are healthy"
break
fi
fi

echo "Waiting for services to become healthy... ($elapsed/$timeout)"
sleep 10
elapsed=$((elapsed + 10))
done

if [ $elapsed -ge $timeout ]; then
echo "Services failed to become healthy"
docker-compose -f docker-compose.e2e.yml ps
docker-compose -f docker-compose.e2e.yml logs
exit 1
fi

- name: Run end-to-end tests
timeout-minutes: 5
run: |
cd services/e2e
python -m pytest test_full_pipeline.py -v --timeout=300

- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: e2e-test-results
path: |
services/e2e/htmlcov/
services/e2e/coverage.xml

- name: Cleanup services
if: always()
run: |
docker-compose -f docker-compose.e2e.yml down --remove-orphans
docker-compose -f docker-compose.e2e.yml rm -f

# Build and push Docker images
build-images:
name: Build Docker Images
runs-on: ubuntu-latest
Expand Down
Loading
Loading