Skip to content

⚡ Bolt: Optimize JSON parsing in Pydantic schemas #709

⚡ Bolt: Optimize JSON parsing in Pydantic schemas

⚡ Bolt: Optimize JSON parsing in Pydantic schemas #709

Workflow file for this run

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