Skip to content

feat: [Backend] Data Quality Monitoring with native compute, multi-backend support, REST API, CLI #26118

feat: [Backend] Data Quality Monitoring with native compute, multi-backend support, REST API, CLI

feat: [Backend] Data Quality Monitoring with native compute, multi-backend support, REST API, CLI #26118

name: pr-integration-tests
on:
pull_request_target:
types:
- opened
- synchronize
- labeled
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
actions: write
pull-requests: read
jobs:
integration-test-python:
# when using pull_request_target, all jobs MUST have this if check for 'ok-to-test' or 'approved' for security purposes.
if:
((github.event.action == 'labeled' && (github.event.label.name == 'approved' || github.event.label.name == 'lgtm' || github.event.label.name == 'ok-to-test')) ||
(github.event.action != 'labeled' && (contains(github.event.pull_request.labels.*.name, 'ok-to-test') || contains(github.event.pull_request.labels.*.name, 'approved') || contains(github.event.pull_request.labels.*.name, 'lgtm')))) &&
github.repository == 'feast-dev/feast'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: [ "3.11" ]
os: [ ubuntu-latest ]
env:
OS: ${{ matrix.os }}
PYTHON: ${{ matrix.python-version }}
services:
redis:
image: redis
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
with:
# pull_request_target runs the workflow in the context of the base repo
# as such actions/checkout needs to be explicit configured to retrieve
# code from the PR.
ref: refs/pull/${{ github.event.pull_request.number }}/merge
submodules: recursive
persist-credentials: false
- name: Setup Python
uses: actions/setup-python@v5
id: setup-python
with:
python-version: ${{ matrix.python-version }}
architecture: x64
- name: Authenticate to Google Cloud
uses: 'google-github-actions/auth@v2'
with:
credentials_json: '${{ secrets.GCP_SA_KEY }}'
- name: Set up gcloud SDK
uses: google-github-actions/setup-gcloud@v2
with:
project_id: ${{ secrets.GCP_PROJECT_ID }}
- name: Use gcloud CLI
run: gcloud info
- name: Set up AWS SDK
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- name: Use AWS CLI
run: aws sts get-caller-identity
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: Install dependencies
run: make install-python-dependencies-ci
- name: Setup Redis Cluster
run: |
docker pull vishnunair/docker-redis-cluster:latest
docker run -d -p 6001:6379 -p 6002:6380 -p 6003:6381 -p 6004:6382 -p 6005:6383 -p 6006:6384 --name redis-cluster vishnunair/docker-redis-cluster
- name: Test python
if: ${{ always() }} # this will guarantee that step won't be canceled and resources won't leak
env:
FEAST_SERVER_DOCKER_IMAGE_TAG: ${{ needs.build-docker-image.outputs.DOCKER_IMAGE_TAG }}
SNOWFLAKE_CI_DEPLOYMENT: ${{ secrets.SNOWFLAKE_CI_DEPLOYMENT }}
SNOWFLAKE_CI_USER: ${{ secrets.SNOWFLAKE_CI_USER }}
SNOWFLAKE_CI_PASSWORD: ${{ secrets.SNOWFLAKE_CI_PASSWORD }}
SNOWFLAKE_CI_ROLE: ${{ secrets.SNOWFLAKE_CI_ROLE }}
SNOWFLAKE_CI_WAREHOUSE: ${{ secrets.SNOWFLAKE_CI_WAREHOUSE }}
run: make test-python-integration
- name: Minimize uv cache
run: uv cache prune --ci
mcp-feature-server-runtime:
if:
((github.event.action == 'labeled' && (github.event.label.name == 'approved' || github.event.label.name == 'lgtm' || github.event.label.name == 'ok-to-test')) ||
(github.event.action != 'labeled' && (contains(github.event.pull_request.labels.*.name, 'ok-to-test') || contains(github.event.pull_request.labels.*.name, 'approved') || contains(github.event.pull_request.labels.*.name, 'lgtm')))) &&
github.repository == 'feast-dev/feast'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
submodules: recursive
persist-credentials: false
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
architecture: x64
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: Install dependencies
run: make install-python-dependencies-ci
- name: Start feature server (MCP HTTP)
run: |
cd examples/mcp_feature_store
uv run python -m feast.cli.cli serve --host 127.0.0.1 --port 6566 --workers 1 --no-access-log &
SERVER_PID=$!
echo $SERVER_PID > /tmp/feast_server_pid
for i in $(seq 1 60); do
kill -0 "$SERVER_PID" || { echo "server died"; exit 1; }
if curl -fsS http://127.0.0.1:6566/health >/dev/null; then
break
fi
sleep 1
done
curl -fsS http://127.0.0.1:6566/health >/dev/null
- name: Validate MCP endpoint
run: |
rm -f /tmp/mcp_headers /tmp/mcp_headers2 /tmp/mcp_body2
curl -sS -D /tmp/mcp_headers -o /dev/null --max-time 10 \
-X POST \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
-H "mcp-protocol-version: 2025-03-26" \
--data '{}' \
http://127.0.0.1:6566/mcp
SESSION_ID=$(grep -i "^mcp-session-id:" /tmp/mcp_headers | head -1 | awk '{print $2}' | tr -d '\r')
if [ -z "${SESSION_ID}" ]; then
cat /tmp/mcp_headers || true
exit 1
fi
curl -sS -D /tmp/mcp_headers2 -o /tmp/mcp_body2 --max-time 10 \
-X POST \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
-H "mcp-protocol-version: 2025-03-26" \
-H "mcp-session-id: ${SESSION_ID}" \
--data '{}' \
http://127.0.0.1:6566/mcp
grep -Eq "^HTTP/.* 400" /tmp/mcp_headers2
grep -Eiq "^content-type: application/json" /tmp/mcp_headers2
grep -Eiq "^mcp-session-id: ${SESSION_ID}" /tmp/mcp_headers2
- name: Stop feature server
if: always()
run: |
if [ -f /tmp/feast_server_pid ]; then
kill "$(cat /tmp/feast_server_pid)" || true
fi
- name: Minimize uv cache
if: always()
run: uv cache prune --ci