Add opt-in free-session budget gate for unauthenticated HTTP traffic #1956
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
| # .github/workflows/ci.yml | |
| name: CI - Run Unit Tests | |
| # This workflow runs on every push to any branch, and on any pull requests. | |
| on: | |
| push: | |
| branches: | |
| - '**' | |
| pull_request: | |
| jobs: | |
| test: | |
| name: Run Unit Tests (${{ matrix.ambient }}) | |
| # The job will run on the latest version of Ubuntu | |
| runs-on: ubuntu-latest | |
| # Backstop: if pytest-timeout fails to kill a hung test, don't let the | |
| # job run for the full GitHub Actions default (6 hours). | |
| timeout-minutes: 5 | |
| # This creates a build matrix to test against multiple Python versions, | |
| # ensuring broad compatibility. The `ambient` dimension (issue #436) runs | |
| # the suite once from a clean checkout and once under a deliberately | |
| # hostile ambient configuration, so the unit suite's determinism is | |
| # continuously enforced. | |
| strategy: | |
| matrix: | |
| python-version: ["3.12"] | |
| ambient: ["clean", "adverse"] | |
| steps: | |
| # Step 1: Check out the repository code so the workflow can access it. | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| persist-credentials: false | |
| # Step 2: Set up the specified Python version from the matrix. | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| # Step 3: Cache dependencies to speed up subsequent runs. | |
| # The cache is invalidated if the Python version or pyproject.toml changes. | |
| - name: Cache pip dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip-${{ matrix.python-version }}- | |
| # Step 4: Install the project dependencies, including the [test] extras. | |
| - name: Bake bundled skill metadata | |
| run: python scripts/bake_skill_metadata.py | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[test]" | |
| # Deliberately hostile ambient configuration (issue #436): the unit suite | |
| # must give the same verdict regardless of local .env contents. Every | |
| # ServerConfig field is set to a valid non-default value so that any test | |
| # asserting on any operator-configurable setting would catch a leak; | |
| # extend this list when adding new fields to ServerConfig. | |
| - name: Write adverse .env | |
| if: matrix.ambient == 'adverse' | |
| run: | | |
| cat > .env <<'EOF' | |
| BLOCKSCOUT_BS_TIMEOUT=0.123 | |
| BLOCKSCOUT_BS_LIGHT_TIMEOUT=0.456 | |
| BLOCKSCOUT_BS_REQUEST_MAX_RETRIES=9 | |
| BLOCKSCOUT_BENS_URL=https://adverse-bens.example.com | |
| BLOCKSCOUT_BENS_TIMEOUT=1.5 | |
| BLOCKSCOUT_CHAINSCOUT_URL=https://adverse-chains.example.com | |
| BLOCKSCOUT_CHAINSCOUT_TIMEOUT=1.5 | |
| BLOCKSCOUT_PRO_API_BASE_URL=https://adverse-pro.example.com/ | |
| BLOCKSCOUT_PRO_API_CONFIG_TIMEOUT=1.5 | |
| BLOCKSCOUT_PRO_API_CONFIG_TTL_SECONDS=11 | |
| BLOCKSCOUT_PRO_API_CONFIG_REFRESH_RETRY_SECONDS=2 | |
| BLOCKSCOUT_PRO_API_KEY=adverse-pro-api-key | |
| BLOCKSCOUT_PRO_API_KEY_HEADER=X-Adverse-Pro-Key | |
| BLOCKSCOUT_PRO_API_KEY_REQUIRED_NOTICE=CI adverse notice | |
| BLOCKSCOUT_PRO_API_LOW_CREDITS_THRESHOLD=1 | |
| BLOCKSCOUT_METADATA_TIMEOUT=1.5 | |
| BLOCKSCOUT_CHAINS_LIST_TTL_SECONDS=11 | |
| BLOCKSCOUT_PROGRESS_INTERVAL_SECONDS=0.5 | |
| BLOCKSCOUT_CONTRACTS_CACHE_MAX_NUMBER=1 | |
| BLOCKSCOUT_CONTRACTS_CACHE_TTL_SECONDS=11 | |
| BLOCKSCOUT_NFT_PAGE_SIZE=3 | |
| BLOCKSCOUT_LOGS_PAGE_SIZE=3 | |
| BLOCKSCOUT_ADVANCED_FILTERS_PAGE_SIZE=3 | |
| BLOCKSCOUT_DIRECT_API_RESPONSE_SIZE_LIMIT=1234 | |
| BLOCKSCOUT_RPC_REQUEST_TIMEOUT=1.5 | |
| BLOCKSCOUT_RPC_POOL_PER_HOST=1 | |
| BLOCKSCOUT_MCP_USER_AGENT=Adverse Agent | |
| BLOCKSCOUT_MCP_ALLOWED_HOSTS=adverse.example.com | |
| BLOCKSCOUT_MCP_ALLOWED_ORIGINS=https://adverse.example.com | |
| BLOCKSCOUT_MIXPANEL_TOKEN=adverse-token | |
| BLOCKSCOUT_MIXPANEL_API_HOST=api.mixpanel.com | |
| BLOCKSCOUT_DISABLE_COMMUNITY_TELEMETRY=true | |
| BLOCKSCOUT_MCP_TRANSPORT=http | |
| BLOCKSCOUT_DEV_JSON_RESPONSE=true | |
| BLOCKSCOUT_INTERMEDIARY_HEADER=X-Adverse-Intermediary | |
| BLOCKSCOUT_INTERMEDIARY_ALLOWLIST=AdverseClient | |
| PORT=4242 | |
| EOF | |
| # Step 5: Run the unit tests with code coverage. | |
| # Pytest will automatically run all tests that are NOT marked with @pytest.mark.integration. | |
| # This is the core of our "fast feedback" strategy. | |
| - name: Run unit tests with coverage (clean) | |
| if: matrix.ambient == 'clean' | |
| run: | | |
| pytest --cov=blockscout_mcp_server --cov-report=xml | |
| - name: Run unit tests with coverage (adverse) | |
| if: matrix.ambient == 'adverse' | |
| env: | |
| BLOCKSCOUT_PRO_API_KEY_REQUIRED_NOTICE: "CI adverse notice" | |
| # Lowercase on purpose: pydantic-settings matches environment variables | |
| # case-insensitively, so the pristine_config fixture must neutralize | |
| # non-uppercase spellings too. | |
| blockscout_bs_timeout: "7.5" | |
| # The `port` field reads the unprefixed PORT variable via its alias; | |
| # exercise that channel through the exported environment as well. | |
| PORT: "4242" | |
| run: | | |
| pytest --cov=blockscout_mcp_server --cov-report=xml |