Skip to content

feat: standardized offline A/B benchmark template for feature validation #121

feat: standardized offline A/B benchmark template for feature validation

feat: standardized offline A/B benchmark template for feature validation #121

Workflow file for this run

name: CI - PR Checks
on:
pull_request:
branches:
- main
jobs:
# Detect if PR contains code changes (skip expensive checks for docs-only PRs)
check-code-changes:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
has_code_changes: ${{ steps.filter.outputs.code }}
steps:
- name: Checkout source
uses: actions/checkout@v6
- name: Check for code changes
uses: dorny/paths-filter@v4
id: filter
with:
filters: |
code:
- '!docs/**'
- '!**/*.md'
- '!LICENSE'
- '!OWNERS'
# Lint: runs all pre-commit hooks (ruff, shellcheck, hadolint, markdownlint, yamllint, etc.)
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
- name: Run pre-commit
uses: pre-commit/action@v3.0.1
# Require VERSION bump when code changes
version-check:
runs-on: ubuntu-latest
needs: check-code-changes
if: needs.check-code-changes.outputs.has_code_changes == 'true'
steps:
- name: Checkout source
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check VERSION was bumped
run: |
changed=$(git diff --name-only origin/main...HEAD \
| grep -E '^(common|prediction|training)/' \
| grep -vE 'requirements\.(in|txt)$' \
| grep -v 'Dockerfile$' || true)
if [ -z "$changed" ]; then
echo "No source changes in common/, prediction/, or training/. Skipping version check."
exit 0
fi
echo "Source files changed:"
echo "$changed"
if ! git diff --name-only origin/main...HEAD | grep -q '^VERSION$'; then
echo "::error::Source changes detected but VERSION was not bumped. Please update the VERSION file."
exit 1
fi
new_version=$(cat VERSION)
old_version=$(git show origin/main:VERSION 2>/dev/null || echo "0.0.0")
if [ "$new_version" = "$old_version" ]; then
echo "::error::VERSION file was modified but the version ($new_version) is the same as main."
exit 1
fi
highest=$(printf '%s\n%s\n' "$old_version" "$new_version" | sort -V | tail -n1)
if [ "$highest" != "$new_version" ]; then
echo "::error::VERSION ($new_version) is not greater than main ($old_version)."
exit 1
fi
echo "VERSION bumped: $old_version -> $new_version"
# Verify the restructured layout is importable
import-check:
runs-on: ubuntu-latest
needs: check-code-changes
if: needs.check-code-changes.outputs.has_code_changes == 'true'
steps:
- name: Checkout source
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
# find_spec instead of import_module: prediction_server.py creates
# /local_models at import time (ModelSyncer.__init__), which fails
# outside a container. Switch to import_module after the lifespan refactor.
- name: Check imports
run: |
pip install -r requirements.txt --quiet
python -c "
import importlib.util
for mod in ['prediction.prediction_server', 'training.training_server', 'common.types']:
spec = importlib.util.find_spec(mod)
assert spec is not None, f'{mod} not found'
print(f'{mod}: {spec.origin}')
"
# Container: build (no push) to validate all service Dockerfiles
container-build:
runs-on: ubuntu-latest
needs: check-code-changes
if: needs.check-code-changes.outputs.has_code_changes == 'true'
strategy:
matrix:
service:
- name: prediction
dockerfile: prediction/Dockerfile
- name: training
dockerfile: training/Dockerfile
- name: test
dockerfile: tests/Dockerfile
steps:
- name: Checkout source
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build ${{ matrix.service.name }} container (no push)
run: |
docker buildx build \
--platform linux/amd64 \
-f ${{ matrix.service.dockerfile }} \
--tag test-build-${{ matrix.service.name }}:pr-${{ github.event.pull_request.number }} \
.