feat: add tenant_schedule database table for manifest-driven scheduling #1015
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Database Migrations | |
| on: | |
| push: | |
| branches: [develop, main] | |
| paths: | |
| - 'services/*/migrations/**' | |
| - 'services/*/atlas/**' | |
| - 'shared/atlas/**' | |
| - 'utilities/atlas-loader/**' | |
| - 'shared/domain/models/**' | |
| - 'scripts/migrate-all-orgs.sh' | |
| pull_request: | |
| branches: [develop, main] | |
| paths: | |
| - 'services/*/migrations/**' | |
| - 'services/*/atlas/**' | |
| - 'shared/atlas/**' | |
| - 'utilities/atlas-loader/**' | |
| - 'shared/domain/models/**' | |
| - 'scripts/migrate-all-orgs.sh' | |
| permissions: | |
| contents: read | |
| jobs: | |
| verify-migrations: | |
| name: Verify Migration Checksums | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Set up Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.26.1' | |
| cache: true | |
| - name: Install Atlas CLI | |
| run: | | |
| curl -sSf https://atlasgo.sh | sh -s -- --yes | |
| atlas version | |
| timeout-minutes: 5 | |
| - name: Verify migration checksums | |
| run: | | |
| echo "Verifying migration checksums..." | |
| atlas migrate hash --env ci --config file://services/current-account/atlas/atlas.hcl | |
| atlas migrate hash --env ci --config file://services/position-keeping/atlas/atlas.hcl | |
| atlas migrate hash --env ci --config file://services/financial-accounting/atlas/atlas.hcl | |
| atlas migrate hash --env ci --config file://services/payment-order/atlas/atlas.hcl | |
| atlas migrate hash --env ci --config file://services/party/atlas/atlas.hcl | |
| atlas migrate hash --env ci --config file://services/tenant/atlas/atlas.hcl | |
| echo "✓ All migration checksums verified" | |
| test-multi-org-migrations: | |
| name: Test Multi-Organization Migrations | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| services: | |
| postgres: | |
| image: postgres:16 | |
| env: | |
| POSTGRES_PASSWORD: postgres | |
| POSTGRES_DB: meridian_test | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 5432:5432 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Set up Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.26.1' | |
| cache: true | |
| - name: Install Atlas CLI | |
| run: | | |
| curl -sSf https://atlasgo.sh | sh -s -- --yes | |
| atlas version | |
| timeout-minutes: 5 | |
| - name: Create test organization schemas | |
| env: | |
| DATABASE_URL: postgres://postgres:postgres@localhost:5432/meridian_test?sslmode=disable | |
| run: | | |
| echo "Creating test organization schemas..." | |
| # In production: database-per-service + schema-per-org | |
| # Each service has its own database, within which each org gets a schema | |
| # For CI: single database, test schema-per-org isolation | |
| psql "$DATABASE_URL" -c "CREATE SCHEMA IF NOT EXISTS org_acme_bank;" | |
| psql "$DATABASE_URL" -c "CREATE SCHEMA IF NOT EXISTS org_test_org;" | |
| # Create service-specific schemas for Atlas revision tracking | |
| # This prevents cross-service version confusion when sharing org schemas | |
| for SERVICE in current_account financial_accounting party payment_order position_keeping; do | |
| psql "$DATABASE_URL" -c "CREATE SCHEMA IF NOT EXISTS atlas_${SERVICE}_revisions;" | |
| done | |
| echo "✓ Test organization schemas created" | |
| - name: Apply migrations to test organizations | |
| env: | |
| DATABASE_URL: postgres://postgres:postgres@localhost:5432/meridian_test?sslmode=disable | |
| run: | | |
| echo "Applying migrations to test organizations..." | |
| echo "Architecture: database-per-service + schema-per-org" | |
| echo "- In production: each service has its own database" | |
| echo "- Within each database: each org gets its own schema" | |
| echo "- Tables use singular, unqualified names (search_path routing)" | |
| echo "- In CI: services get separate schemas to mirror production separation" | |
| echo "" | |
| FAILED=0 | |
| for ORG in acme_bank test_org; do | |
| echo "Migrating organization: $ORG..." | |
| for CONFIG in services/current-account/atlas/atlas.hcl \ | |
| services/financial-accounting/atlas/atlas.hcl \ | |
| services/party/atlas/atlas.hcl \ | |
| services/payment-order/atlas/atlas.hcl \ | |
| services/position-keeping/atlas/atlas.hcl; do | |
| SERVICE=$(basename $(dirname $(dirname "$CONFIG"))) | |
| SERVICE_UNDERSCORE=$(echo "$SERVICE" | tr '-' '_') | |
| # Each service gets its own schema per org (mirrors production database-per-service) | |
| ORG_SCHEMA="${SERVICE_UNDERSCORE}_org_${ORG}" | |
| REVISION_SCHEMA="atlas_${SERVICE_UNDERSCORE}_revisions" | |
| echo " Service: $SERVICE (schema: $ORG_SCHEMA, revisions: $REVISION_SCHEMA)" | |
| # Create service-specific org schema if it doesn't exist | |
| psql "$DATABASE_URL" -c "CREATE SCHEMA IF NOT EXISTS ${ORG_SCHEMA};" 2>&1 | grep -v "CREATE SCHEMA" || true | |
| # --revisions-schema isolates each service's revision tracking | |
| # In production, each service has its own database | |
| if ! atlas migrate apply \ | |
| --env ci \ | |
| --config "file://$CONFIG" \ | |
| --url "${DATABASE_URL}&search_path=${ORG_SCHEMA}" \ | |
| --revisions-schema "$REVISION_SCHEMA" \ | |
| --tx-mode none; then | |
| echo " ✗ Migration failed for $SERVICE in $ORG_SCHEMA" | |
| FAILED=1 | |
| fi | |
| done | |
| done | |
| if [ "$FAILED" -eq 1 ]; then | |
| echo "❌ Some migrations failed" | |
| exit 1 | |
| fi | |
| echo "" | |
| echo "✓ Migrations applied successfully" | |
| - name: Verify organization isolation and table naming | |
| env: | |
| DATABASE_URL: postgres://postgres:postgres@localhost:5432/meridian_test?sslmode=disable | |
| run: | | |
| echo "Verifying service-per-org schemas and table naming..." | |
| echo "Each service gets its own schema per org (mirrors production database-per-service)" | |
| # Define expected tables per service (all use unqualified/singular names) | |
| declare -A SERVICE_TABLES | |
| SERVICE_TABLES[current_account]="account lien audit_log audit_outbox" | |
| SERVICE_TABLES[financial_accounting]="financial_booking_log ledger_posting audit_log audit_outbox" | |
| SERVICE_TABLES[party]="party party_association party_demographic party_reference party_bank_relation audit_log audit_outbox" | |
| SERVICE_TABLES[payment_order]="payment_order audit_log audit_outbox" | |
| SERVICE_TABLES[position_keeping]="financial_position_log audit_trail_entry transaction_lineage transaction_log_entry audit_log audit_outbox" | |
| for ORG in acme_bank test_org; do | |
| echo "" | |
| echo "Verifying organization: $ORG" | |
| for SERVICE in current_account financial_accounting party payment_order position_keeping; do | |
| SCHEMA="${SERVICE}_org_${ORG}" | |
| echo " Schema: $SCHEMA" | |
| # Verify schema exists | |
| SCHEMA_EXISTS=$(psql "$DATABASE_URL" -t -c " | |
| SELECT COUNT(*) FROM information_schema.schemata | |
| WHERE schema_name = '$SCHEMA'; | |
| " | xargs) | |
| if [ "$SCHEMA_EXISTS" -eq 0 ]; then | |
| echo " ✗ Schema does not exist!" | |
| continue | |
| fi | |
| # Count tables | |
| TABLE_COUNT=$(psql "$DATABASE_URL" -t -c " | |
| SELECT COUNT(*) FROM information_schema.tables | |
| WHERE table_schema = '$SCHEMA' AND table_type = 'BASE TABLE'; | |
| " | xargs) | |
| echo " Tables: $TABLE_COUNT" | |
| # Verify expected tables exist | |
| for TABLE in ${SERVICE_TABLES[$SERVICE]}; do | |
| EXISTS=$(psql "$DATABASE_URL" -t -c " | |
| SELECT COUNT(*) FROM information_schema.tables | |
| WHERE table_schema = '$SCHEMA' AND table_name = '$TABLE'; | |
| " | xargs) | |
| if [ "$EXISTS" -gt 0 ]; then | |
| echo " ✓ $TABLE" | |
| fi | |
| done | |
| done | |
| done | |
| # Verify NO old schema-qualified patterns exist | |
| echo "" | |
| echo "Verifying no old schema-qualified tables..." | |
| OLD_SCHEMAS="current_account financial_accounting payment_order position_keeping platform" | |
| for SCHEMA in $OLD_SCHEMAS; do | |
| EXISTS=$(psql "$DATABASE_URL" -t -c " | |
| SELECT COUNT(*) FROM information_schema.schemata | |
| WHERE schema_name = '$SCHEMA'; | |
| " | xargs) | |
| if [ "$EXISTS" -gt 0 ]; then | |
| TABLES=$(psql "$DATABASE_URL" -t -c " | |
| SELECT COUNT(*) FROM information_schema.tables | |
| WHERE table_schema = '$SCHEMA'; | |
| " | xargs) | |
| if [ "$TABLES" -gt 0 ]; then | |
| echo " ❌ Found $TABLES tables in old schema '$SCHEMA'" | |
| exit 1 | |
| fi | |
| fi | |
| done | |
| echo " ✓ No old schema-qualified tables found" | |
| echo "" | |
| echo "✓ Organization isolation and table naming verified" |