Skip to content
This repository was archived by the owner on Aug 29, 2026. It is now read-only.

chore(deps): bump the npm-frontend group across 1 directory with 58 updates #4338

chore(deps): bump the npm-frontend group across 1 directory with 58 updates

chore(deps): bump the npm-frontend group across 1 directory with 58 updates #4338

name: Public API Test
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
# ───────────────────────────────────────────────────────────────────
# Build: compile the backend Docker image once, share via artifact
# ───────────────────────────────────────────────────────────────────
build:
if: >-
github.event_name != 'pull_request' ||
(github.event.pull_request.head.repo.full_name == github.repository &&
github.actor != 'dependabot[bot]')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Build test backend image
run: |
echo "Building backend image for testing..."
docker build -t test-backend:latest ./backend
- name: Export backend image
run: docker save test-backend:latest | gzip > /tmp/test-backend-image.tar.gz
- name: Upload backend image
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: test-backend-image
path: /tmp/test-backend-image.tar.gz
retention-days: 1
# ───────────────────────────────────────────────────────────────────
# Test: 5 parallel runners, each with its own Docker Compose stack
# ───────────────────────────────────────────────────────────────────
test-public-api:
needs: build
runs-on: ubuntu-latest
environment: dev
strategy:
fail-fast: false
matrix:
group: [0, 1, 2, 3, 4]
steps:
- name: Verify prerequisites
run: |
echo "Docker version:"
docker --version
echo "Docker Compose version:"
docker compose version
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.13"
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
cd backend
pip install -r tests/e2e/requirements.txt
- name: Download backend image
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: test-backend-image
path: /tmp
- name: Load backend image
run: |
echo "Loading pre-built backend image..."
docker load < /tmp/test-backend-image.tar.gz
docker images test-backend
- name: Pre-pull heavy Docker images
run: |
echo "Pre-pulling Docker images in parallel..."
echo "Note: text2vec-transformers disabled via docker-compose profile (backend uses OpenAI)"
docker pull postgres:16 &
docker pull redis:7-alpine &
docker pull temporalio/auto-setup:1.24.2 &
docker pull temporalio/ui:2.26.2 &
docker pull vespaengine/vespa:8 &
docker pull svix/svix-server &
docker pull alpine:3.19 &
docker pull ghcr.io/docling-project/docling-serve &
wait
echo "All images pre-pulled successfully"
- name: Setup environment and start services
env:
BACKEND_IMAGE: test-backend:latest
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }}
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY || 'dummy-key' }}
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY || 'dummy-key' }}
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY || 'dummy-key' }}
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY || 'dummy-key' }}
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY || 'dummy-key' }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY || 'dummy-key' }}
# Required for start.sh to skip interactive prompts
NONINTERACTIVE: "1"
# Skip services not needed for backend API tests
SKIP_LOCAL_EMBEDDINGS: "1" # Use OpenAI embeddings (saves ~2GB)
SKIP_FRONTEND: "1" # Backend-only testing (saves time)
SKIP_CONNECT: "1" # Backend-only testing (saves time)
# Enable docling-serve for OCR fallback testing
ENABLE_DOCLING: "1"
# Set required environment variables for the containers
AUTH0_ENABLED: "false"
DEV_MODE: "true"
ENABLE_INTERNAL_SOURCES: "true"
run: |
# Create .env from example
cp .env.example .env
# Add AI keys to .env before starting services
echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env
echo "STRIPE_API_KEY=$STRIPE_API_KEY" >> .env
echo "MISTRAL_API_KEY=$MISTRAL_API_KEY" >> .env
echo "COHERE_API_KEY=$COHERE_API_KEY" >> .env
echo "GROQ_API_KEY=$GROQ_API_KEY" >> .env
echo "CEREBRAS_API_KEY=$CEREBRAS_API_KEY" >> .env
echo "TOGETHER_API_KEY=$TOGETHER_API_KEY" >> .env
echo "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" >> .env
# Point backend at the docling-serve container
echo "DOCLING_BASE_URL=http://docling:5001" >> .env
# Disable rate limiting for regular tests
echo "DISABLE_RATE_LIMIT=true" >> .env
# Enable internal sources (stub, snapshot) for testing
echo "ENABLE_INTERNAL_SOURCES=true" >> .env
# Disable webhook endpoint verification (tests use fake URLs)
echo "WEBHOOK_VERIFY_ENDPOINTS=false" >> .env
# Set embedding config (OpenAI text-embedding-3-small, 1536 dims)
echo "DENSE_EMBEDDER=openai_text_embedding_3_small" >> .env
echo "EMBEDDING_DIMENSIONS=1536" >> .env
echo "SPARSE_EMBEDDER=fastembed_bm25" >> .env
echo "Starting services using start.sh..."
./start.sh --noninteractive
# The script already does health checks, but let's verify
echo ""
echo "Verifying we're using the correct test images:"
docker ps --format "table {{.Names}}\t{{.Image}}" | grep airweave
echo ""
echo "Final verification of services:"
docker ps
# Backend runs on port 8001 according to start.sh
echo "Testing backend on port 8001..."
curl -f http://localhost:8001/health || (echo "Backend not healthy"; docker logs airweave-backend; exit 1)
echo "Backend is healthy and ready for API tests!"
- name: Verify embedding configuration
run: |
echo "Checking embedding stack configuration..."
# Verify embedding validation passed at startup (no errors in logs)
if docker logs airweave-backend 2>&1 | grep -q "EmbeddingConfigurationError"; then
echo "ERROR: Embedding configuration failed!"
docker logs airweave-backend 2>&1 | grep -A5 "EmbeddingConfigurationError"
exit 1
fi
# Check for successful embedding validation message
if docker logs airweave-backend 2>&1 | grep -q "Embedding stack validated"; then
echo "✓ Embedding stack validated successfully"
docker logs airweave-backend 2>&1 | grep "Embedding stack" | head -5
else
echo "Note: Embedding validation message not found (may be at DEBUG level)"
fi
# Verify Vespa schema was deployed with correct dimensions
echo ""
echo "Checking Vespa deployment..."
VESPA_DIMS=$(docker logs airweave-vespa-init 2>&1 | grep -o "EMBEDDING_DIMENSIONS=[0-9]*" | cut -d= -f2 || echo "unknown")
if [ "$VESPA_DIMS" = "1536" ]; then
echo "✓ Vespa deployed with EMBEDDING_DIMENSIONS=1536"
elif [ "$VESPA_DIMS" = "unknown" ]; then
echo "Note: Could not verify Vespa dimensions from init logs"
else
echo "WARNING: Vespa may have unexpected dimensions: $VESPA_DIMS"
fi
echo ""
echo "Embedding configuration verification complete!"
- name: Run E2E Smoke Tests (group ${{ matrix.group }})
env:
TEST_ENV: local
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
TEST_STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }}
# Required test environment variables
TEST_NOTION_TOKEN: ${{ secrets.TEST_NOTION_TOKEN }}
TEST_GOOGLE_CLIENT_ID: ${{ secrets.TEST_GOOGLE_CLIENT_ID }}
TEST_GOOGLE_CLIENT_SECRET: ${{ secrets.TEST_GOOGLE_CLIENT_SECRET }}
# OAuth1 (Trello) credentials
TEST_TRELLO_CONSUMER_KEY: ${{ secrets.TEST_TRELLO_CONSUMER_KEY }}
TEST_TRELLO_CONSUMER_SECRET: ${{ secrets.TEST_TRELLO_CONSUMER_SECRET }}
TEST_AUTH_PROVIDER_NAME: composio
TEST_COMPOSIO_API_KEY: ${{ secrets.TEST_COMPOSIO_API_KEY }}
# Composio service-specific configurations
TEST_COMPOSIO_TODOIST_AUTH_CONFIG_ID: ac_wcspYMD22UDD
TEST_COMPOSIO_TODOIST_ACCOUNT_ID: ca_h1xDM7cdVzeI
TEST_COMPOSIO_ASANA_AUTH_CONFIG_ID: ac_hBi29B_-iU3B
TEST_COMPOSIO_ASANA_ACCOUNT_ID: ca_ooqwRcKOwHmo
TEST_COMPOSIO_GMAIL_AUTH_CONFIG_ID: ac_HocwjtTv-dqb
TEST_COMPOSIO_GMAIL_ACCOUNT_ID: ca_jEA4l17nSPzN
# Pipedream configurations (regular tests)
TEST_PIPEDREAM_CLIENT_ID: ${{ secrets.TEST_PIPEDREAM_CLIENT_ID }}
TEST_PIPEDREAM_CLIENT_SECRET: ${{ secrets.TEST_PIPEDREAM_CLIENT_SECRET }}
TEST_PIPEDREAM_PROJECT_ID: ${{ secrets.TEST_PIPEDREAM_PROJECT_ID }}
TEST_PIPEDREAM_ACCOUNT_ID: ${{ secrets.TEST_PIPEDREAM_ACCOUNT_ID }}
TEST_PIPEDREAM_EXTERNAL_USER_ID: ${{ secrets.TEST_PIPEDREAM_EXTERNAL_USER_ID }}
# Pipedream configurations (rate limit tests - separate project)
TEST_PIPEDREAM_RATE_LIMIT_CLIENT_ID: ${{ secrets.TEST_PIPEDREAM_RATE_LIMIT_CLIENT_ID }}
TEST_PIPEDREAM_RATE_LIMIT_CLIENT_SECRET: ${{ secrets.TEST_PIPEDREAM_RATE_LIMIT_CLIENT_SECRET }}
TEST_PIPEDREAM_RATE_LIMIT_PROJECT_ID: ${{ secrets.TEST_PIPEDREAM_RATE_LIMIT_PROJECT_ID }}
TEST_PIPEDREAM_GOOGLE_DRIVE_DEFAULT_OAUTH_ACCOUNT_ID: ${{ secrets.TEST_PIPEDREAM_GOOGLE_DRIVE_DEFAULT_OAUTH_ACCOUNT_ID }}
TEST_PIPEDREAM_GOOGLE_DRIVE_DEFAULT_OAUTH_EXTERNAL_USER_ID: ${{ secrets.TEST_PIPEDREAM_GOOGLE_DRIVE_DEFAULT_OAUTH_EXTERNAL_USER_ID }}
TEST_PIPEDREAM_NOTION_DEFAULT_OAUTH_ACCOUNT_ID: ${{ secrets.TEST_PIPEDREAM_NOTION_DEFAULT_OAUTH_ACCOUNT_ID }}
TEST_PIPEDREAM_NOTION_DEFAULT_OAUTH_EXTERNAL_USER_ID: ${{ secrets.TEST_PIPEDREAM_NOTION_DEFAULT_OAUTH_EXTERNAL_USER_ID }}
# Composio configurations (rate limit tests)
TEST_COMPOSIO_NOTION_AUTH_CONFIG_ID_1: ${{ secrets.TEST_COMPOSIO_NOTION_AUTH_CONFIG_ID_1 }}
TEST_COMPOSIO_NOTION_ACCOUNT_ID_1: ${{ secrets.TEST_COMPOSIO_NOTION_ACCOUNT_ID_1 }}
TEST_COMPOSIO_NOTION_AUTH_CONFIG_ID_2: ${{ secrets.TEST_COMPOSIO_NOTION_AUTH_CONFIG_ID_2 }}
TEST_COMPOSIO_NOTION_ACCOUNT_ID_2: ${{ secrets.TEST_COMPOSIO_NOTION_ACCOUNT_ID_2 }}
TEST_COMPOSIO_GOOGLE_DRIVE_AUTH_CONFIG_ID_1: ${{ secrets.TEST_COMPOSIO_GOOGLE_DRIVE_AUTH_CONFIG_ID_1 }}
TEST_COMPOSIO_GOOGLE_DRIVE_ACCOUNT_ID_1: ${{ secrets.TEST_COMPOSIO_GOOGLE_DRIVE_ACCOUNT_ID_1 }}
TEST_COMPOSIO_GOOGLE_DRIVE_AUTH_CONFIG_ID_2: ${{ secrets.TEST_COMPOSIO_GOOGLE_DRIVE_AUTH_CONFIG_ID_2 }}
TEST_COMPOSIO_GOOGLE_DRIVE_ACCOUNT_ID_2: ${{ secrets.TEST_COMPOSIO_GOOGLE_DRIVE_ACCOUNT_ID_2 }}
# Override images to use locally built test images
BACKEND_IMAGE: test-backend:latest
run: |
cd backend
# ── Dynamically discover and split test files ──
# All matrix jobs see the same file list. We shuffle deterministically
# using the commit SHA so each run gets a different (but reproducible)
# distribution, and each job picks its own slice via round-robin.
COMMIT_SHA="${GITHUB_SHA:-$(date +%Y%m%d)}"
GROUP=${{ matrix.group }}
TOTAL_GROUPS=5
# Collect all smoke test files (excluding rate-limit-only files)
ALL_FILES=$(find tests/e2e/smoke -maxdepth 1 -name 'test_*.py' \
! -name 'test_rate_limiting.py' \
! -name 'test_source_rate_limiting.py' \
| sort)
# Shuffle deterministically: hash each filename with the commit SHA,
# sort by hash, then strip the hash prefix
SHUFFLED=$(echo "$ALL_FILES" | while read -r f; do
hash=$(echo "${f}${COMMIT_SHA}" | md5sum | cut -c1-8)
echo "$hash $f"
done | sort | awk '{print $2}')
TOTAL=$(echo "$SHUFFLED" | wc -l)
# Round-robin: file at position i goes to group (i % TOTAL_GROUPS)
TEST_FILES=$(echo "$SHUFFLED" | awk -v g="$GROUP" -v tg="$TOTAL_GROUPS" '(NR - 1) % tg == g {print}' | tr '\n' ' ')
FILE_COUNT=$(echo "$TEST_FILES" | wc -w)
echo "=========================================="
echo "Group $GROUP of $TOTAL_GROUPS: running $FILE_COUNT / $TOTAL test files"
echo "=========================================="
echo "$TEST_FILES" | tr ' ' '\n' | grep -v '^$'
echo "=========================================="
WORKERS=2
MAX_RETRIES=2
# Run tests for this group
# --dist loadscope keeps all tests from the same module/class on one
# worker, so module-scoped fixtures (e.g. synced_file_stub) are only
# created once instead of once per worker.
set +e
pytest $TEST_FILES -m "not rate_limit and not api_rate_limit" -n $WORKERS --dist loadscope -v --tb=short 2>&1 | tee pytest_output.txt
EXIT_CODE=${PIPESTATUS[0]}
set -e
if [ $EXIT_CODE -eq 0 ]; then
echo "All tests passed! (group $GROUP)"
exit 0
fi
# Count failures from pytest output
FAILURES=$(grep -oP '\d+(?= failed)' pytest_output.txt | head -1 || echo "0")
echo "Detected $FAILURES test failure(s) in group $GROUP"
# If too many failures, don't retry (likely a real issue, not flakiness)
if [ "$FAILURES" -ge 4 ]; then
echo "4 or more tests failed in group $GROUP. Not retrying."
exit 1
fi
# Retry only the failed tests
ATTEMPT=1
while [ $ATTEMPT -le $MAX_RETRIES ]; do
echo ""
echo "=========================================="
echo "Retry attempt $ATTEMPT of $MAX_RETRIES (group $GROUP, ONLY failed tests)"
echo "=========================================="
sleep 10
set +e
pytest $TEST_FILES -m "not rate_limit and not api_rate_limit" --lf -n $WORKERS --dist loadscope -v --tb=short 2>&1 | tee pytest_retry_output.txt
EXIT_CODE=${PIPESTATUS[0]}
set -e
if [ $EXIT_CODE -eq 0 ]; then
echo "Failed tests passed on retry! (group $GROUP)"
exit 0
fi
ATTEMPT=$((ATTEMPT + 1))
done
# If we get here, tests failed after retries
echo "Tests in group $GROUP failed after $MAX_RETRIES retry attempts"
exit 1
- name: Run Source Rate Limit Tests (Sequential)
if: false # Temporarily disabled to reduce CI load
env:
TEST_ENV: local
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
TEST_STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }}
# Required test environment variables (needed by TestSettings even for rate limit tests)
TEST_NOTION_TOKEN: ${{ secrets.TEST_NOTION_TOKEN }}
TEST_GOOGLE_CLIENT_ID: ${{ secrets.TEST_GOOGLE_CLIENT_ID }}
TEST_GOOGLE_CLIENT_SECRET: ${{ secrets.TEST_GOOGLE_CLIENT_SECRET }}
TEST_TRELLO_CONSUMER_KEY: ${{ secrets.TEST_TRELLO_CONSUMER_KEY }}
TEST_TRELLO_CONSUMER_SECRET: ${{ secrets.TEST_TRELLO_CONSUMER_SECRET }}
TEST_AUTH_PROVIDER_NAME: composio
TEST_COMPOSIO_API_KEY: ${{ secrets.TEST_COMPOSIO_API_KEY }}
# Pipedream configurations (regular tests - required by TestSettings)
TEST_PIPEDREAM_CLIENT_ID: ${{ secrets.TEST_PIPEDREAM_CLIENT_ID }}
TEST_PIPEDREAM_CLIENT_SECRET: ${{ secrets.TEST_PIPEDREAM_CLIENT_SECRET }}
TEST_PIPEDREAM_PROJECT_ID: ${{ secrets.TEST_PIPEDREAM_PROJECT_ID }}
TEST_PIPEDREAM_ACCOUNT_ID: ${{ secrets.TEST_PIPEDREAM_ACCOUNT_ID }}
TEST_PIPEDREAM_EXTERNAL_USER_ID: ${{ secrets.TEST_PIPEDREAM_EXTERNAL_USER_ID }}
# Composio configurations (rate limit tests)
TEST_COMPOSIO_NOTION_AUTH_CONFIG_ID_1: ${{ secrets.TEST_COMPOSIO_NOTION_AUTH_CONFIG_ID_1 }}
TEST_COMPOSIO_NOTION_ACCOUNT_ID_1: ${{ secrets.TEST_COMPOSIO_NOTION_ACCOUNT_ID_1 }}
TEST_COMPOSIO_NOTION_AUTH_CONFIG_ID_2: ${{ secrets.TEST_COMPOSIO_NOTION_AUTH_CONFIG_ID_2 }}
TEST_COMPOSIO_NOTION_ACCOUNT_ID_2: ${{ secrets.TEST_COMPOSIO_NOTION_ACCOUNT_ID_2 }}
TEST_COMPOSIO_GOOGLE_DRIVE_AUTH_CONFIG_ID_1: ${{ secrets.TEST_COMPOSIO_GOOGLE_DRIVE_AUTH_CONFIG_ID_1 }}
TEST_COMPOSIO_GOOGLE_DRIVE_ACCOUNT_ID_1: ${{ secrets.TEST_COMPOSIO_GOOGLE_DRIVE_ACCOUNT_ID_1 }}
TEST_COMPOSIO_GOOGLE_DRIVE_AUTH_CONFIG_ID_2: ${{ secrets.TEST_COMPOSIO_GOOGLE_DRIVE_AUTH_CONFIG_ID_2 }}
TEST_COMPOSIO_GOOGLE_DRIVE_ACCOUNT_ID_2: ${{ secrets.TEST_COMPOSIO_GOOGLE_DRIVE_ACCOUNT_ID_2 }}
# Pipedream configurations (rate limit tests - separate project)
TEST_PIPEDREAM_RATE_LIMIT_CLIENT_ID: ${{ secrets.TEST_PIPEDREAM_RATE_LIMIT_CLIENT_ID }}
TEST_PIPEDREAM_RATE_LIMIT_CLIENT_SECRET: ${{ secrets.TEST_PIPEDREAM_RATE_LIMIT_CLIENT_SECRET }}
TEST_PIPEDREAM_RATE_LIMIT_PROJECT_ID: ${{ secrets.TEST_PIPEDREAM_RATE_LIMIT_PROJECT_ID }}
TEST_PIPEDREAM_GOOGLE_DRIVE_DEFAULT_OAUTH_ACCOUNT_ID: ${{ secrets.TEST_PIPEDREAM_GOOGLE_DRIVE_DEFAULT_OAUTH_ACCOUNT_ID }}
TEST_PIPEDREAM_GOOGLE_DRIVE_DEFAULT_OAUTH_EXTERNAL_USER_ID: ${{ secrets.TEST_PIPEDREAM_GOOGLE_DRIVE_DEFAULT_OAUTH_EXTERNAL_USER_ID }}
TEST_PIPEDREAM_NOTION_DEFAULT_OAUTH_ACCOUNT_ID: ${{ secrets.TEST_PIPEDREAM_NOTION_DEFAULT_OAUTH_ACCOUNT_ID }}
TEST_PIPEDREAM_NOTION_DEFAULT_OAUTH_EXTERNAL_USER_ID: ${{ secrets.TEST_PIPEDREAM_NOTION_DEFAULT_OAUTH_EXTERNAL_USER_ID }}
run: |
cd backend
echo "Running source rate limit tests sequentially (no parallelization for Redis isolation)..."
pytest tests/e2e/smoke -m rate_limit -v --tb=short
- name: Print service logs on failure (group ${{ matrix.group }})
if: failure()
run: |
echo "=========================================="
echo "FAILED GROUP: ${{ matrix.group }}"
echo "=========================================="
echo ""
echo "=========================================="
echo "CONTAINER STATUS"
echo "=========================================="
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo ""
echo "=========================================="
echo "SVIX SERVER LOGS (last 400 lines)"
echo "=========================================="
docker logs airweave-svix --tail 400 2>&1 || echo "Could not fetch Svix logs"
echo ""
echo "=========================================="
echo "FASTAPI SERVER LOGS (last 1000 lines)"
echo "=========================================="
docker logs airweave-backend --tail 1000 2>&1 || echo "Could not fetch backend logs"
echo ""
echo "=========================================="
echo "TEMPORAL WORKER LOGS (last 2000 lines)"
echo "=========================================="
docker logs airweave-temporal-worker --tail 2000 2>&1 || echo "Could not fetch worker logs"
echo ""
echo "=========================================="
echo "ERROR / WARNING LINES (worker, last 200 lines)"
echo "=========================================="
docker logs airweave-temporal-worker 2>&1 | grep -iE "ERROR|WARNING|WARN|exception|traceback" | tail -200 || echo "No error/warning lines found"
echo ""
echo "=========================================="
echo "ERROR / WARNING LINES (backend, last 200 lines)"
echo "=========================================="
docker logs airweave-backend 2>&1 | grep -iE "ERROR|WARNING|WARN|exception|traceback" | tail -200 || echo "No error/warning lines found"
- name: Cleanup Docker containers
if: always()
run: |
echo "Cleaning up Docker containers..."
docker compose down -v || true
docker system prune -f || true