feat: establish peer database runtime contract #75
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: Repository and artifact checks | |
| on: | |
| pull_request: | |
| push: | |
| branches: | |
| - develop | |
| - main | |
| workflow_dispatch: | |
| inputs: | |
| preview_pr: | |
| description: Trusted pull request number to deploy after checks pass | |
| required: false | |
| type: number | |
| permissions: | |
| checks: read | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| group: repository-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| PDM_CHECK_UPDATE: "false" | |
| jobs: | |
| dependency-review: | |
| name: Dependency security review | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Reject new high-severity vulnerabilities | |
| uses: actions/dependency-review-action@v5.0.0 | |
| with: | |
| fail-on-severity: high | |
| fail-on-scopes: development, runtime | |
| repository: | |
| name: Hermetic repository contract | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Python and PDM | |
| uses: pdm-project/setup-pdm@v4.5 | |
| with: | |
| python-version-file: .python-version | |
| version: 2.27.0 | |
| cache: true | |
| - name: Install locked dependencies | |
| run: pdm install -G dev --frozen-lockfile | |
| - name: Run repository contract | |
| run: pdm run check | |
| - name: Enforce append-only migration history | |
| if: github.event_name == 'pull_request' | |
| run: pdm run check:migration-history "origin/${{ github.base_ref }}" | |
| - name: Enforce append-only migration history on push | |
| if: github.event_name == 'push' | |
| run: pdm run check:migration-history "${{ github.event.before }}" | |
| - name: Run pre-commit contract | |
| run: pdm run pre-commit run --all-files | |
| artifact: | |
| name: Portable peer database runtime | |
| runs-on: ubuntu-latest | |
| services: | |
| postgres: | |
| image: >- | |
| pgvector/pgvector:pg17@sha256:d2ef61f42ef767baa5a1475393303cc235bcd92febd9d7014eddb48b41f3bad0 | |
| env: | |
| POSTGRES_DB: inkcre | |
| POSTGRES_USER: postgres | |
| POSTGRES_PASSWORD: postgres | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -U postgres -d inkcre" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 12 | |
| env: | |
| CORE_DATABASE_PASSWORD: ci-core-database-password-at-least-32-bytes | |
| DATABASE_URL: >- | |
| postgresql+psycopg://inkcre_core:ci-core-database-password-at-least-32-bytes@127.0.0.1:5432/inkcre | |
| INKCRE_ENV_FILE: "" | |
| JWT_SECRET: ci-development-jwt-secret-at-least-32-bytes | |
| MIGRATION_DATABASE_URL: >- | |
| postgresql+psycopg://postgres:postgres@127.0.0.1:5432/inkcre | |
| OBSRV__LOGGING_BACKEND: none | |
| POSTGREST_DATABASE_PASSWORD: ci-postgrest-database-password-at-least-32-bytes | |
| POSTGREST_IMAGE: >- | |
| postgrest/postgrest:v14.15@sha256:2f8e7b656f09db697a8875177694b417b35cb76c21370de07fc54e711e902326 | |
| IMAGE_TAG: inkcre-core:${{ github.sha }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Build frozen artifact | |
| run: >- | |
| docker build | |
| --build-arg "SOURCE_REVISION=${{ github.sha }}" | |
| --tag "$IMAGE_TAG" | |
| . | |
| - name: Inspect artifact contents | |
| run: >- | |
| docker run --rm --entrypoint python "$IMAGE_TAG" -c | |
| "from pathlib import Path; | |
| required = ( | |
| 'app', 'libs', 'utils', 'extensions', 'migrations/versions', | |
| 'alembic.ini', 'scripts/container.py', 'scripts/database.py', | |
| 'scripts/verify_postgrest_contract.py', 'data/ai/prompts' | |
| ); | |
| missing = [path for path in required if not Path(path).exists()]; | |
| assert not missing, missing" | |
| - name: Initialize fresh database twice | |
| run: | | |
| docker run --rm --network host \ | |
| --env CORE_DATABASE_PASSWORD \ | |
| --env MIGRATION_DATABASE_URL \ | |
| --env POSTGREST_DATABASE_PASSWORD \ | |
| "$IMAGE_TAG" db init --profile development | |
| docker run --rm --network host \ | |
| --env CORE_DATABASE_PASSWORD \ | |
| --env MIGRATION_DATABASE_URL \ | |
| --env POSTGREST_DATABASE_PASSWORD \ | |
| "$IMAGE_TAG" db init --profile development | |
| - name: Verify migration, metadata, and machine readiness | |
| run: | | |
| docker run --rm --network host \ | |
| --env MIGRATION_DATABASE_URL \ | |
| "$IMAGE_TAG" db ready --profile development --json | |
| docker run --rm --network host --env MIGRATION_DATABASE_URL \ | |
| --entrypoint alembic "$IMAGE_TAG" check | |
| - name: Prove readiness rejects contract drift | |
| run: | | |
| docker run --rm --network host \ | |
| --env CORE_DATABASE_PASSWORD \ | |
| --env MIGRATION_DATABASE_URL \ | |
| --env POSTGREST_DATABASE_PASSWORD \ | |
| --entrypoint python \ | |
| "$IMAGE_TAG" \ | |
| scripts/verify_database_contract_failures.py | |
| - name: Start PostgREST and verify peer read/write contract | |
| run: | | |
| postgrest_database_url="postgresql://authenticator:${POSTGREST_DATABASE_PASSWORD}@127.0.0.1:5432/inkcre" | |
| postgrest_id="$( | |
| docker run --detach --network host \ | |
| --env "PGRST_DB_URI=$postgrest_database_url" \ | |
| --env PGRST_DB_SCHEMAS=inkcre \ | |
| --env PGRST_DB_ANON_ROLE=anonymous \ | |
| --env PGRST_DB_PRE_REQUEST=inkcre_internal.check_jwt \ | |
| --env "PGRST_JWT_SECRET=$JWT_SECRET" \ | |
| --env PGRST_JWT_AUD=inkcre-api \ | |
| --env PGRST_SERVER_PORT=13000 \ | |
| "$POSTGREST_IMAGE" | |
| )" | |
| trap 'docker logs "$postgrest_id"; docker rm --force "$postgrest_id"' EXIT | |
| for _ in $(seq 1 30); do | |
| if docker run --rm --network host \ | |
| "$IMAGE_TAG" \ | |
| db contract --json >/dev/null && | |
| docker run --rm --network host \ | |
| --entrypoint python \ | |
| "$IMAGE_TAG" \ | |
| scripts/verify_postgrest_contract.py \ | |
| --base-url http://127.0.0.1:13000 \ | |
| --jwt-secret "$JWT_SECRET" \ | |
| --wrong-jwt-secret ci-wrong-jwt-secret-at-least-32-bytes; then | |
| exit 0 | |
| fi | |
| sleep 1 | |
| done | |
| exit 1 | |
| - name: Verify deterministic reset twice | |
| run: | | |
| fingerprint() { | |
| docker run --rm --network host \ | |
| --env MIGRATION_DATABASE_URL \ | |
| --entrypoint python \ | |
| "$IMAGE_TAG" \ | |
| -c "from app.database_contract.catalog import development_baseline_fingerprint as f; print(f())" | |
| } | |
| baseline="$(fingerprint)" | |
| docker run --rm --network host \ | |
| --env CORE_DATABASE_PASSWORD \ | |
| --env MIGRATION_DATABASE_URL \ | |
| --env POSTGREST_DATABASE_PASSWORD \ | |
| "$IMAGE_TAG" db reset-dev --confirm reset-development-data | |
| test "$(fingerprint)" = "$baseline" | |
| docker run --rm --network host \ | |
| --env CORE_DATABASE_PASSWORD \ | |
| --env MIGRATION_DATABASE_URL \ | |
| --env POSTGREST_DATABASE_PASSWORD \ | |
| "$IMAGE_TAG" db reset-dev --confirm reset-development-data | |
| test "$(fingerprint)" = "$baseline" | |
| - name: Start and probe web artifact | |
| run: | | |
| container_id="$( | |
| docker run --detach --network host \ | |
| --env DATABASE_URL \ | |
| --env JWT_SECRET \ | |
| --env INKCRE_ENV_FILE \ | |
| --env MIGRATION_DATABASE_URL \ | |
| --env PORT=18080 \ | |
| "$IMAGE_TAG" web | |
| )" | |
| trap 'docker logs "$container_id"; docker rm --force "$container_id"' EXIT | |
| for _ in $(seq 1 30); do | |
| if curl --fail --silent http://127.0.0.1:18080/livez >/dev/null; then | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| curl --fail --silent http://127.0.0.1:18080/livez | |
| curl --fail --silent http://127.0.0.1:18080/readyz | |
| preview: | |
| name: Deploy trusted preview | |
| if: github.event_name == 'workflow_dispatch' && inputs.preview_pr != 0 | |
| needs: | |
| - repository | |
| - artifact | |
| runs-on: ubuntu-latest | |
| environment: preview | |
| steps: | |
| - name: Checkout trusted bootstrap source | |
| uses: actions/checkout@v5 | |
| with: | |
| ref: ${{ github.sha }} | |
| persist-credentials: false | |
| - name: Verify exact pull request head | |
| uses: ./.github/actions/preview-verify | |
| with: | |
| github_token: ${{ github.token }} | |
| head_sha: ${{ github.sha }} | |
| pr_number: ${{ inputs.preview_pr }} | |
| - name: Build web and release images without deployment secrets | |
| env: | |
| HEAD_SHA: ${{ github.sha }} | |
| run: | | |
| docker build \ | |
| --target heroku-web \ | |
| --tag "inkcre-preview-web:$HEAD_SHA" \ | |
| . | |
| docker build \ | |
| --target heroku-release \ | |
| --tag "inkcre-preview-release:$HEAD_SHA" \ | |
| . | |
| - name: Deliver preview | |
| uses: ./.github/actions/preview-delivery | |
| with: | |
| core_database_password: ${{ secrets.CORE_DATABASE_PASSWORD }} | |
| head_sha: ${{ github.sha }} | |
| heroku_api_key: ${{ secrets.HEROKU_API_KEY }} | |
| llm_sp_ak: ${{ secrets.LLM_SP_AK }} | |
| llm_sp_base_url: ${{ secrets.LLM_SP_BASE_URL }} | |
| neon_api_key: ${{ secrets.NEON_API_KEY }} | |
| neon_project_id: ${{ vars.NEON_PROJECT_ID }} | |
| postgrest_database_password: ${{ secrets.POSTGREST_DATABASE_PASSWORD }} | |
| pr_number: ${{ inputs.preview_pr }} |