release: 0.43.0 - Polish Under Pressure (day-two batch) #106
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: PR Tests | |
| on: | |
| pull_request: | |
| paths: | |
| - "src/**" | |
| - "tests/**" | |
| - "alembic/**" | |
| - "alembic.ini" | |
| - "dashboard/**" | |
| - "deploy/cookbooks/**" | |
| - "cf-sandbox-worker/**" | |
| - "Dockerfile" | |
| - "Dockerfile.runner" | |
| - "e2b.Dockerfile" | |
| - "docker-compose*.yml" | |
| - "docker-compose*.yaml" | |
| - ".dockerignore" | |
| - ".env.example" | |
| - "pyproject.toml" | |
| - "uv.lock" | |
| - ".github/workflows/pr-tests.yml" | |
| workflow_dispatch: | |
| concurrency: | |
| group: pr-tests-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| python: | |
| name: Python tests | |
| runs-on: ubuntu-latest | |
| # ~17k tests run in ~13.5 min plus dashboard build and dependency install; | |
| # 20 min leaves headroom over the total setup + run time. | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| cache-dependency-path: dashboard/package-lock.json | |
| - name: Build dashboard (force-included by hatchling) | |
| working-directory: dashboard | |
| run: | | |
| npm ci | |
| npm run build | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| - name: Install package + test extras | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[dev,mcp,memory,otel,parse,report,docker,security]" | |
| - name: Validate Compose files | |
| run: | | |
| python - <<'PY' | |
| from pathlib import Path | |
| import yaml | |
| for name in ( | |
| "docker-compose.yml", | |
| "docker-compose.local.yml", | |
| "docker-compose.infra.yml", | |
| "docker-compose.spark.yml", | |
| ): | |
| data = yaml.safe_load(Path(name).read_text()) | |
| services = data.get("services") or {} | |
| if not services: | |
| raise SystemExit(f"{name} has no services") | |
| print(f"{name}: {', '.join(sorted(services))}") | |
| PY | |
| - name: Run pytest | |
| run: | | |
| python -m pytest tests/ -q --tb=line --timeout=60 | |
| docker-build: | |
| name: Docker images + API smoke test | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Build application image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: Dockerfile | |
| load: true | |
| tags: sandcastle-ai:ci | |
| cache-from: type=gha,scope=sandcastle-main | |
| cache-to: type=gha,mode=max,scope=sandcastle-main | |
| - name: Build sandbox runner image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: Dockerfile.runner | |
| load: true | |
| tags: sandcastle-runner:ci | |
| cache-from: type=gha,scope=sandcastle-runner | |
| cache-to: type=gha,mode=max,scope=sandcastle-runner | |
| - name: Build E2B sandbox image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: e2b.Dockerfile | |
| load: false | |
| tags: sandcastle-e2b:ci | |
| cache-from: type=gha,scope=sandcastle-e2b | |
| cache-to: type=gha,mode=max,scope=sandcastle-e2b | |
| - name: Start local-mode API and wait for health endpoint | |
| run: | | |
| docker run -d --name sandcastle-smoke \ | |
| -p 127.0.0.1:18080:8080 \ | |
| -e SANDCASTLE_ALLOW_INSECURE_BIND=true \ | |
| -e AUTO_UPDATE_CHECK=false \ | |
| sandcastle-ai:ci | |
| for attempt in $(seq 1 45); do | |
| if curl --fail --silent --show-error http://127.0.0.1:18080/api/health >/dev/null; then | |
| exit 0 | |
| fi | |
| sleep 2 | |
| done | |
| docker logs sandcastle-smoke | |
| exit 1 | |
| - name: Collect smoke-test logs and clean up | |
| if: always() | |
| run: | | |
| docker logs sandcastle-smoke || true | |
| docker rm --force sandcastle-smoke || true | |
| postgres-migrations: | |
| name: PostgreSQL migrations | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| services: | |
| postgres: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_USER: sandcastle | |
| POSTGRES_PASSWORD: sandcastle | |
| POSTGRES_DB: sandcastle | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -U sandcastle -d sandcastle" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| env: | |
| DATABASE_URL: postgresql+asyncpg://sandcastle:sandcastle@localhost:5432/sandcastle | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| - name: Install package | |
| run: | | |
| python -m pip install --upgrade pip | |
| # hatchling force-includes dashboard/dist; an empty dir satisfies it | |
| # without pulling the whole Node toolchain into this job. | |
| mkdir -p dashboard/dist | |
| pip install -e ".[dev]" | |
| - name: Apply migrations (customer CLI path) | |
| run: python -m sandcastle db migrate | |
| - name: Re-run migrations (container restart scenario) | |
| run: python -m sandcastle db migrate | |
| - name: Check migration metadata drift | |
| run: python -m alembic check | |
| - name: Smoke PostgreSQL enum persistence | |
| run: | | |
| POSTGRES_MIGRATIONS_DATABASE_URL="$DATABASE_URL" \ | |
| python -m pytest tests/test_postgres_enum_smoke.py -q --tb=short --timeout=120 | |
| - name: Verify current revision is the migration head | |
| run: | | |
| current_revision="$(python -m alembic current)" | |
| head_revision="$(python -m alembic heads | awk 'NR == 1 { print $1 }')" | |
| printf '%s\n' "$current_revision" | |
| grep -Fq "$head_revision" <<< "$current_revision" | |
| dashboard: | |
| name: Dashboard build + tests | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| cache-dependency-path: dashboard/package-lock.json | |
| - name: Install | |
| working-directory: dashboard | |
| run: npm ci | |
| - name: TypeScript typecheck | |
| working-directory: dashboard | |
| run: npx tsc -b --noEmit | |
| - name: ESLint (advisory - existing baseline has unresolved errors) | |
| working-directory: dashboard | |
| run: npm run lint || echo "::warning::ESLint reported issues; advisory only until baseline cleanup PR lands" | |
| continue-on-error: true | |
| - name: Vitest | |
| working-directory: dashboard | |
| run: npx vitest run | |
| - name: Production build | |
| working-directory: dashboard | |
| run: npm run build | |
| cf-sandbox-worker: | |
| name: Cloudflare Sandbox worker | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| defaults: | |
| run: | |
| working-directory: cf-sandbox-worker | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| cache-dependency-path: cf-sandbox-worker/package-lock.json | |
| - name: Install | |
| run: npm ci | |
| - name: TypeScript typecheck | |
| run: npx tsc --noEmit | |
| - name: Validate Wrangler configuration | |
| # The Docker build below validates the image; skipping rollout keeps | |
| # this dry-run account-independent. | |
| run: npx wrangler deploy --dry-run --containers-rollout=none --outdir /tmp/wrangler-dry | |
| - name: Build Sandbox container | |
| run: docker build -t sandcastle-cf-sandbox-test . |