| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| name: CI |
| |
| on: |
| pull_request: |
| push: |
| branches: |
| - master |
| workflow_dispatch: |
| |
| permissions: |
| contents: read |
| |
| concurrency: |
| group: ci-${{ github.workflow }}-${{ github.ref }} |
| cancel-in-progress: true |
| |
| jobs: |
| quality: |
| name: Quality |
| runs-on: ubuntu-latest |
| timeout-minutes: 20 |
| steps: |
| - name: Check out repository |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 |
| with: |
| persist-credentials: false |
| - name: Check Apache license headers |
| uses: apache/skywalking-eyes/header@315732dd4b8d3a015d8d9b91936b935a0b854817 |
| with: |
| token: "" |
| - name: Set up Python |
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 |
| with: |
| python-version: "3.12" |
| - name: Install uv |
| run: >- |
| python -m pip install --disable-pip-version-check --no-deps |
| uv==0.9.0 |
| - name: Verify lock file |
| run: uv lock --check |
| - name: Sync development environment |
| run: uv sync --frozen --group dev |
| - name: Verify generated 1.0 tool catalog |
| run: uv run python generate_tool_catalog.py --check |
| - name: Ruff |
| run: uv run ruff check . |
| - name: Mypy |
| run: uv run mypy doris_mcp_server |
| - name: Bandit |
| run: >- |
| uv run bandit -q -c pyproject.toml -r |
| doris_mcp_server doris_mcp_client generate_requirements.py |
| generate_tool_catalog.py |
| |
| test: |
| name: Tests |
| runs-on: ubuntu-latest |
| timeout-minutes: 30 |
| steps: |
| - name: Check out repository |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 |
| with: |
| persist-credentials: false |
| - name: Set up Python |
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 |
| with: |
| python-version: "3.12" |
| - name: Install uv |
| run: >- |
| python -m pip install --disable-pip-version-check --no-deps |
| uv==0.9.0 |
| - name: Sync development environment |
| run: uv sync --frozen --group dev |
| - name: Run full test suite |
| run: uv run pytest -q -W error |
| - name: Enforce domain coverage floors |
| run: | |
| uv run coverage json -o "$RUNNER_TEMP/coverage.json" |
| uv run python test/deployment/check_coverage_domains.py \ |
| "$RUNNER_TEMP/coverage.json" |
| |
| package: |
| name: Build and wheel smoke |
| runs-on: ubuntu-latest |
| timeout-minutes: 20 |
| steps: |
| - name: Check out repository |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 |
| with: |
| persist-credentials: false |
| - name: Set up Python |
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 |
| with: |
| python-version: "3.12" |
| - name: Install uv |
| run: >- |
| python -m pip install --disable-pip-version-check --no-deps |
| uv==0.9.0 |
| - name: Sync development environment |
| run: uv sync --frozen --group dev |
| - name: Build distributions |
| run: uv build |
| - name: Install and smoke-test clean wheel |
| run: | |
| set -euo pipefail |
| wheel_count="$(find dist -maxdepth 1 -type f -name '*.whl' | wc -l | tr -d ' ')" |
| test "$wheel_count" = "1" |
| wheel_path="$(find dist -maxdepth 1 -type f -name '*.whl' -print -quit)" |
| smoke_root="$RUNNER_TEMP/doris-mcp-wheel-smoke" |
| python -m venv "$smoke_root/venv" |
| "$smoke_root/venv/bin/python" -m pip install "$GITHUB_WORKSPACE/$wheel_path" |
| cd "$smoke_root" |
| "$smoke_root/venv/bin/doris-mcp-server" --version |
| "$smoke_root/venv/bin/doris-mcp-client" --help |
| "$smoke_root/venv/bin/python" -c \ |
| "import doris_mcp_client, doris_mcp_server" |
| "$smoke_root/venv/bin/python" \ |
| "$GITHUB_WORKSPACE/test/deployment/check_runtime_dependencies.py" |
| |
| conformance: |
| name: MCP 2026-07-28 conformance |
| runs-on: ubuntu-latest |
| timeout-minutes: 20 |
| steps: |
| - name: Check out repository |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 |
| with: |
| persist-credentials: false |
| - name: Check out official MCP Conformance |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 |
| with: |
| repository: modelcontextprotocol/conformance |
| ref: 49103de6ed70804e940637bf3e9e29e4a3f54e64 |
| path: .conformance |
| persist-credentials: false |
| - name: Set up Python |
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 |
| with: |
| python-version: "3.12" |
| - name: Install uv |
| run: >- |
| python -m pip install --disable-pip-version-check --no-deps |
| uv==0.9.0 |
| - name: Set up Node.js |
| uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 |
| with: |
| node-version: "24" |
| cache: npm |
| cache-dependency-path: .conformance/package-lock.json |
| - name: Sync development environment |
| run: uv sync --frozen --group dev |
| - name: Install and build official Conformance |
| working-directory: .conformance |
| run: | |
| npm ci |
| npm run build |
| - name: Run official server-stateless scenario |
| run: | |
| set -euo pipefail |
| server_log="$RUNNER_TEMP/doris-mcp-conformance-server.log" |
| uv run python test/protocol/conformance_server.py \ |
| --transport http \ |
| --host 127.0.0.1 \ |
| --port 39124 \ |
| >"$server_log" 2>&1 & |
| server_pid="$!" |
| cleanup() { |
| kill "$server_pid" 2>/dev/null || true |
| } |
| trap cleanup EXIT |
| |
| ready=false |
| for _ in $(seq 1 30); do |
| if python -c \ |
| 'import socket; s = socket.create_connection(("127.0.0.1", 39124), 1); s.close()' |
| then |
| ready=true |
| break |
| fi |
| sleep 1 |
| done |
| if [ "$ready" != "true" ]; then |
| cat "$server_log" |
| exit 1 |
| fi |
| |
| node .conformance/dist/index.js server \ |
| --url http://127.0.0.1:39124/mcp \ |
| --scenario server-stateless \ |
| --verbose |