From 09fab2ecc9a1118245a5b81e6ae3c80ed2e3b77d Mon Sep 17 00:00:00 2001 From: Alicia <115451386+congw729@users.noreply.github.com> Date: Mon, 17 Nov 2025 18:13:05 +0800 Subject: [PATCH] CI Workflows. (#50) Signed-off-by: WANG Cong <115451386+congw729@users.noreply.github.com> --- .github/CODEOWNERS | 17 ++++ .github/scripts/detect_changed_tests.sh | 89 +++++++++++++++++ .github/workflows/ci-all-files.yml | 60 ++++++++++++ .github/workflows/ci.yml | 124 ++++++++++++++++++++++++ .github/workflows/pre-commit.yml | 26 +++++ .pre-commit-config.yaml | 20 +++- pyproject.toml | 44 +++++++-- 7 files changed, 368 insertions(+), 12 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100755 .github/scripts/detect_changed_tests.sh create mode 100644 .github/workflows/ci-all-files.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pre-commit.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..237319481 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,17 @@ +# CODEOWNERS is used to automatically assign PR reviewers +# Format: path pattern @username or @team +# More info: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# Default owners (fallback reviewers for all files) +# later, we can add more reviewers here +* @hsliuustc0106 + +# Assign reviewers by directory +/.github/ @owner1 +/tests/ @owner1 +/docs/ @owner1 + +# Example: reviewers for specific directories +# /examples/offline_inference/ @offline-inference-team +# /vllm_omni/core/ @core-team +# /vllm_omni/model_executor/ @model-team diff --git a/.github/scripts/detect_changed_tests.sh b/.github/scripts/detect_changed_tests.sh new file mode 100755 index 000000000..a3e7518e2 --- /dev/null +++ b/.github/scripts/detect_changed_tests.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# Script to detect changed files and find corresponding test files +# Usage: ./detect_changed_tests.sh [base_sha] [head_sha] [event_type] +# +# Outputs to GITHUB_OUTPUT: +# test_files: Space-separated list of test files to run +# has_tests: 'true' if test files found, 'false' otherwise + +set -e + +BASE_SHA="${1:-}" +HEAD_SHA="${2:-}" +EVENT_TYPE="${3:-push}" + +# Determine base and head SHA based on event type +if [ "$EVENT_TYPE" = "pull_request" ]; then + BASE_SHA="${BASE_SHA:-$GITHUB_BASE_REF}" + HEAD_SHA="${HEAD_SHA:-$GITHUB_SHA}" +elif [ -z "$BASE_SHA" ] || [ -z "$HEAD_SHA" ]; then + # For push events, use git to find the previous commit + BASE_SHA="${BASE_SHA:-$(git rev-parse HEAD~1 2>/dev/null || echo HEAD)}" + HEAD_SHA="${HEAD_SHA:-HEAD}" +fi + +echo "Event type: $EVENT_TYPE" +echo "Base SHA: $BASE_SHA" +echo "Head SHA: $HEAD_SHA" + +# Get changed Python files +CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRT "$BASE_SHA" "$HEAD_SHA" | grep -E '\.(py)$' || true) + +if [ -z "$CHANGED_FILES" ]; then + echo "No Python files changed" + echo "test_files=" >> "$GITHUB_OUTPUT" + echo "has_tests=false" >> "$GITHUB_OUTPUT" + exit 0 +fi + +echo "Changed Python files:" +echo "$CHANGED_FILES" + +# Extract related test files +TEST_FILES="" +for file in $CHANGED_FILES; do + # If changed file is a test file, add it directly + if [[ "$file" == tests/* ]]; then + TEST_FILES="$TEST_FILES $file" + # If changed file is source code, find corresponding test file + elif [[ "$file" == vllm_omni/* ]]; then + # Convert vllm_omni/path/to/module.py to possible test file paths + REL_PATH=$(echo "$file" | sed 's|^vllm_omni/||' | sed 's|\.py$||') + MODULE_NAME=$(basename "$REL_PATH") + DIR_PATH=$(dirname "$REL_PATH") + + # Try multiple possible test file naming patterns + POSSIBLE_TESTS=( + "tests/test_${REL_PATH//\//_}.py" + "tests/${REL_PATH//\//_}_test.py" + ) + + # If multi-level directory, also try directory structure + if [ "$DIR_PATH" != "." ]; then + POSSIBLE_TESTS+=( + "tests/${DIR_PATH}/test_${MODULE_NAME}.py" + "tests/${DIR_PATH}/${MODULE_NAME}_test.py" + ) + fi + + for test_file in "${POSSIBLE_TESTS[@]}"; do + if [ -f "$test_file" ]; then + TEST_FILES="$TEST_FILES $test_file" + break + fi + done + fi +done + +# Deduplicate and format +TEST_FILES=$(echo "$TEST_FILES" | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' ' | sed 's/^ *//;s/ *$//') + +if [ -z "$TEST_FILES" ]; then + echo "No related test files found, will run all tests" + echo "test_files=" >> "$GITHUB_OUTPUT" + echo "has_tests=false" >> "$GITHUB_OUTPUT" +else + echo "Found test files: $TEST_FILES" + echo "test_files=$TEST_FILES" >> "$GITHUB_OUTPUT" + echo "has_tests=true" >> "$GITHUB_OUTPUT" +fi diff --git a/.github/workflows/ci-all-files.yml b/.github/workflows/ci-all-files.yml new file mode 100644 index 000000000..01420ef54 --- /dev/null +++ b/.github/workflows/ci-all-files.yml @@ -0,0 +1,60 @@ +name: CI All Files + +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + test-changed: + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + python-version: ["3.12", "3.13"] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Need full history to detect changes + + - name: Free disk space + run: | + # Remove large, preinstalled toolchains we don't need on the runner + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/hostedtoolcache/CodeQL + # Prune docker to reclaim space (safe on GH runners) + docker system prune -af || true + df -h + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Prepare environment with uv + run: | + python -m pip install --upgrade pip uv + uv venv --python "${{ matrix.python-version }}" --seed + echo "VIRTUAL_ENV=$PWD/.venv" >> $GITHUB_ENV + echo "PATH=$PWD/.venv/bin:$PATH" >> $GITHUB_ENV + source .venv/bin/activate + uv pip install -e ".[dev]" + + - name: Run tests + run: | + if [ ! -d tests ]; then + echo "tests/ directory not found. Skipping pytest run." + + + exit 0 + fi + TEST_FILES=$(find tests -type f \( -name "test_*.py" -o -name "*_test.py" \)) + if [ -z "$TEST_FILES" ]; then + echo "No pytest-compatible test files detected under tests/. Skipping pytest run." + exit 0 + fi + pytest tests/ -v -m "not slow" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..2eaab59fa --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,124 @@ +name: CI + +on: + pull_request: + branches: [main] + workflow_dispatch: + inputs: + test_changed_only: + description: 'Only test changed files (true) or all files (false)' + required: false + default: 'true' + type: choice + options: + - 'true' + - 'false' + enable_changed_files_detection: + description: 'Enable changed files detection feature' + required: false + default: 'true' + type: choice + options: + - 'true' + - 'false' + workflow_call: + inputs: + test_changed_only: + description: 'Only test changed files (true) or all files (false)' + required: false + type: string + default: 'true' + enable_changed_files_detection: + description: 'Enable changed files detection feature' + required: false + type: string + default: 'true' + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + python-version: ["3.12", "3.13"] + steps: + - uses: actions/checkout@v4 + + - name: Free disk space + run: | + # Remove large, preinstalled toolchains we don't need on the runner + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/hostedtoolcache/CodeQL + # Prune docker to reclaim space (safe on GH runners) + docker system prune -af || true + df -h + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Prepare environment with uv + run: | + python -m pip install --upgrade pip uv + uv venv --python "${{ matrix.python-version }}" --seed + echo "VIRTUAL_ENV=$PWD/.venv" >> $GITHUB_ENV + echo "PATH=$PWD/.venv/bin:$PATH" >> $GITHUB_ENV + # Activate env to ensure subsequent commands succeed in this step + source .venv/bin/activate + uv pip install -e ".[dev]" + + - name: Determine test scope + id: test-scope + env: + ENABLE_CHANGED_FILES_DETECTION: ${{ inputs.enable_changed_files_detection || 'true' }} + TEST_CHANGED_ONLY: ${{ inputs.test_changed_only || 'true' }} + run: | + # Determine if we should test only changed files + # If detection is disabled, always test all files + if [ "$ENABLE_CHANGED_FILES_DETECTION" != "true" ]; then + TEST_CHANGED_ONLY="false" + fi + + echo "test_changed_only=$TEST_CHANGED_ONLY" >> $GITHUB_OUTPUT + echo "enable_detection=$ENABLE_CHANGED_FILES_DETECTION" >> $GITHUB_OUTPUT + echo "Test scope: $TEST_CHANGED_ONLY" + echo "Changed files detection: $ENABLE_CHANGED_FILES_DETECTION" + + - name: Get changed test files + id: changed-tests + if: steps.test-scope.outputs.test_changed_only == 'true' && steps.test-scope.outputs.enable_detection == 'true' + env: + GITHUB_BASE_REF: ${{ github.event.pull_request.base.sha || '' }} + GITHUB_SHA: ${{ github.sha }} + run: | + chmod +x .github/scripts/detect_changed_tests.sh + .github/scripts/detect_changed_tests.sh \ + "${{ github.event.pull_request.base.sha || github.event.before || '' }}" \ + "${{ github.sha }}" \ + "${{ github.event_name }}" + + - name: Run tests (changed files only) + if: steps.test-scope.outputs.test_changed_only == 'true' && steps.changed-tests.outputs.has_tests == 'true' + run: | + pytest ${{ steps.changed-tests.outputs.test_files }} -v -m "not slow" + + - name: Run tests (all files) + if: steps.test-scope.outputs.test_changed_only == 'false' || steps.changed-tests.outputs.has_tests == 'false' + run: | + if [ ! -d tests ]; then + echo "tests/ directory not found. Skipping pytest run." + exit 0 + fi + TEST_FILES=$(find tests -type f \( -name "test_*.py" -o -name "*_test.py" \)) + if [ -z "$TEST_FILES" ]; then + echo "No pytest-compatible test files detected under tests/. Skipping pytest run." + exit 0 + fi + pytest tests/ -v -m "not slow" diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 000000000..5decd423c --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,26 @@ +name: pre-commit + +on: + pull_request: + branches: [main] + workflow_dispatch: # for manual trigger + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: 'pip' + - uses: pre-commit/action@v3.0.1 + with: + extra_args: --hook-stage manual diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 585150b27..58f7c1373 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,8 +22,20 @@ repos: hooks: - id: black - - repo: https://github.com/PyCQA/flake8 - rev: 7.1.1 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.11.7 hooks: - - id: flake8 - args: ["--max-line-length=160", "--extend-ignore=E203,W503"] + - id: ruff + args: [--output-format, github, --fix, --line-length=160] + + - repo: https://github.com/crate-ci/typos + rev: v1.35.5 + hooks: + - id: typos + # only for staged files + + - repo: https://github.com/rhysd/actionlint + rev: v1.7.7 + hooks: + - id: actionlint + files: ^\.github/workflows/.*\.ya?ml$ diff --git a/pyproject.toml b/pyproject.toml index b67d3748d..5f0e38f8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,6 @@ dev = [ "pytest-cov>=4.0.0", "black>=23.0.0", "isort>=5.12.0", - "flake8>=6.0.0", "mypy>=1.0.0", "pre-commit>=3.0.0", ] @@ -79,8 +78,8 @@ where = ["."] include = ["vllm_omni*"] [tool.black] -line-length = 88 -target-version = ['py39', 'py310', 'py311', 'py312'] +line-length = 160 +target-version = ['py312', 'py313'] include = '\.pyi?$' extend-exclude = ''' /( @@ -99,15 +98,44 @@ extend-exclude = ''' [tool.isort] profile = "black" multi_line_output = 3 -line_length = 88 +line_length = 160 known_first_party = ["vllm_omni"] -[tool.flake8] -max-line-length = 88 -extend-ignore = ["E203", "W503"] +[tool.ruff] +line-length = 160 +target-version = "py312, py313" +exclude = [ + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".tox", + ".venv", + "build", + "dist", + "vllm_omni.egg-info", +] + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort (handled separately, but included for compatibility) + "N", # pep8-naming + "UP", # pyupgrade +] +ignore = [ + "E203", # whitespace before ':' (conflicts with black) + # W503 is not needed in ruff as it's compatible with black by default +] + +[tool.ruff.lint.per-file-ignores] +"examples/**" = ["E501"] # Allow long lines in examples +"tests/**" = ["E501"] # Allow long lines in tests [tool.mypy] -python_version = "3.9" +python_version = "3.12, 3.13" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true