⚡ Bolt: Optimize JSON parsing in Pydantic schemas #709
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: CI | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - 'src/**' | |
| - 'requirements*.txt' | |
| - 'scripts/**' | |
| - 'Dockerfile' | |
| - '.github/workflows/ci.yml' | |
| pull_request: | |
| branches: [main] | |
| types: [opened, synchronize, reopened, labeled, unlabeled] | |
| paths: | |
| - 'src/**' | |
| - 'requirements*.txt' | |
| - 'scripts/**' | |
| - 'Dockerfile' | |
| - '.github/workflows/ci.yml' | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| jobs: | |
| quality: | |
| name: Code Quality & Tests | |
| runs-on: ubuntu-latest | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| fetch-depth: 0 # Important for Gitleaks to scan history | |
| - name: Gitleaks Scan | |
| uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2.3.9 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| continue-on-error: false # Fail the build if secrets are found | |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 | |
| with: | |
| python-version: "3.11" | |
| cache: "pip" | |
| - name: Install dependencies | |
| run: pip install -r requirements-ci.txt | |
| - name: Lint (ruff) | |
| run: ruff check . | |
| - name: Format check (ruff) | |
| run: ruff format --check . | |
| - name: Type check (mypy) | |
| run: mypy src/app/ | |
| env: | |
| PYTHONPATH: src | |
| - name: Tests (pytest) | |
| run: pytest --cov=src/app --cov-report=term-missing | |
| env: | |
| PYTHONPATH: src | |
| - name: Run pip-audit | |
| run: pip-audit -r requirements-ci.txt | |
| docker-build: | |
| name: Docker Smoke Test | |
| runs-on: ubuntu-latest | |
| needs: [quality, trivy] | |
| timeout-minutes: 10 | |
| if: | | |
| always() && | |
| github.event_name == 'pull_request' && | |
| contains(github.event.pull_request.labels.*.name, 'run-smoke') && | |
| !failure() && !cancelled() | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Free disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/share/powershell /usr/local/.ghcup | |
| docker image prune -af | |
| df -h | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 | |
| - name: Build Docker image | |
| uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 | |
| with: | |
| context: . | |
| tags: pokedex:ci | |
| load: true | |
| cache-from: type=gha,scope=container-build | |
| cache-to: type=gha,mode=max,scope=container-build | |
| - name: Start container | |
| run: | | |
| docker run -d -p 8000:8000 --name pokedex-smoke pokedex:ci | |
| sleep 5 | |
| docker ps | grep -q pokedex-smoke || { docker logs pokedex-smoke; exit 1; } | |
| - name: Smoke test | |
| run: | | |
| # Wait for health check and verify model is loaded | |
| timeout 120 bash -c 'until curl -sf http://localhost:8000/health | jq -e ".model_loaded == true" > /dev/null 2>&1; do | |
| echo "Waiting for model to load..." | |
| sleep 5 | |
| done' | |
| echo "Smoke test passed: API is healthy and model is loaded." | |
| - name: Dump container logs on failure | |
| if: failure() | |
| run: | | |
| docker ps -a | |
| docker logs pokedex-smoke | |
| - name: Stop container | |
| if: always() | |
| run: docker rm -f pokedex-smoke | |
| trivy: | |
| name: Trivy Image Scan | |
| runs-on: ubuntu-latest | |
| needs: quality | |
| timeout-minutes: 15 | |
| if: | | |
| github.event_name == 'pull_request' && | |
| contains(github.event.pull_request.labels.*.name, 'run-trivy') | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Free disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/share/powershell /usr/local/.ghcup | |
| docker image prune -af | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 | |
| - name: Build Docker image | |
| uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 | |
| with: | |
| context: . | |
| tags: pokedex:ci | |
| load: true | |
| cache-from: type=gha,scope=container-build | |
| cache-to: type=gha,mode=max,scope=container-build | |
| - name: Run Trivy vulnerability scanner (image) | |
| uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 | |
| with: | |
| image-ref: 'pokedex:ci' | |
| format: 'sarif' | |
| output: 'trivy-results.sarif' | |
| severity: 'CRITICAL,HIGH,MEDIUM' | |
| exit-code: '1' | |
| ignore-unfixed: true | |
| scanners: 'vuln,misconfig' | |
| limit-severities-for-sarif: false | |
| - name: Upload Trivy scan results as artifact | |
| if: always() | |
| # Note: SARIF upload to Security Tab requires GHAS. Uploading as artifact for public repo. | |
| uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 | |
| with: | |
| name: trivy-report | |
| path: trivy-results.sarif | |
| dast: | |
| name: DAST (Schemathesis) | |
| runs-on: ubuntu-latest | |
| needs: [quality, trivy, docker-build] | |
| timeout-minutes: 10 | |
| # Opt-in only: Only run if the 'run-dast' label is present | |
| if: | | |
| always() && | |
| github.event_name == 'pull_request' && | |
| contains(github.event.pull_request.labels.*.name, 'run-dast') && | |
| !failure() && !cancelled() | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Free disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/share/powershell /usr/local/.ghcup | |
| docker image prune -af | |
| df -h | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 | |
| - name: Build Docker image | |
| uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 | |
| with: | |
| context: . | |
| tags: pokedex:ci | |
| load: true | |
| cache-from: type=gha,scope=container-build | |
| cache-to: type=gha,mode=max,scope=container-build | |
| - name: Start container | |
| run: docker run -d -p 8000:8000 --name pokedex pokedex:ci | |
| - name: Wait for healthy | |
| run: | | |
| if ! docker ps | grep -q pokedex; then | |
| echo "Container failed to start" | |
| docker logs pokedex | |
| exit 1 | |
| fi | |
| timeout 60 bash -c 'until curl -sf http://localhost:8000/health | grep -q "\"status\":\"ok\""; do sleep 2; done' | |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 | |
| with: | |
| python-version: "3.11" | |
| cache: "pip" | |
| - name: Install Schemathesis | |
| run: | | |
| pip install hypothesis==6.115.0 | |
| pip install schemathesis==3.39.6 | |
| - name: Run DAST | |
| run: | | |
| mkdir -p reports | |
| schemathesis run http://localhost:8000/openapi.json \ | |
| --experimental=openapi-3.1 \ | |
| --checks all \ | |
| --hypothesis-max-examples=100 \ | |
| --max-response-time=5000 \ | |
| --junit-xml reports/schemathesis.xml | |
| - name: Upload DAST report | |
| if: always() | |
| uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 | |
| with: | |
| name: dast-report | |
| path: reports/schemathesis.xml | |
| - name: Stop container | |
| if: always() | |
| run: docker rm -f pokedex |