lenskit/lkpy

View on GitHub
.github/workflows/test.yml

Summary

Maintainability
Test Coverage
# GENERATED FILE, do not edit
# This file was generated with Typeline
# generated from: workflows/test.ts
name: Automatic Tests
'on':
  push:
    branches:
      - main
    paths:
      - lenskit*/**.py
      - '**pyproject.toml'
      - requirements*.txt
      - data/**
      - utils/measure-coverage.tcl
      - .github/workflows/test.yml
  pull_request:
    paths:
      - lenskit*/**.py
      - '**pyproject.toml'
      - requirements*.txt
      - data/**
      - utils/measure-coverage.tcl
      - .github/workflows/test.yml
concurrency:
  group: 'test-${{github.ref}}'
  cancel-in-progress: true
jobs:
  conda:
    name: 'Conda Python ${{matrix.python}} on ${{matrix.platform}}'
    runs-on: '${{matrix.platform}}'
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        python:
          - py311
          - py312
        platform:
          - ubuntu-latest
          - macos-latest
          - windows-latest
    steps:
      - name: "🛒 Checkout"
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: prefix-dev/setup-pixi@v0.8.1
        with:
          pixi-version: latest
          activate-environment: true
          environments: 'test-${{matrix.python}}-core'
          cache-write: false
      - name: "🔍 Inspect environment"
        run: |
          python -m lenskit.util.envcheck
      - name: "🏃🏻‍➡️ Test LKPY"
        run: |
          python -m pytest --verbose --log-file=test.log --durations=25 --cov=lenskit/lenskit lenskit/tests
      - name: "📐 Coverage results"
        run: |
          coverage xml
          coverage report
          cp .coverage coverage.db
      - name: "📤 Upload test results"
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: 'test-conda-${{matrix.platform}}-py${{matrix.python}}'
          path: |
            test*.log
            coverage.db
            coverage.xml
  vanilla:
    name: 'Vanilla Python ${{matrix.python}} on ${{matrix.platform}}'
    runs-on: '${{matrix.platform}}'
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        python:
          - '3.11'
          - '3.12'
        platform:
          - ubuntu-latest
          - macos-latest
    steps:
      - name: "🛒 Checkout"
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: "🐍 Set up Python"
        id: install-python
        uses: actions/setup-python@v5
        with:
          python-version: '${{matrix.python}}'
      - name: "🕶️ Set up uv"
        run: |
          pip install -U 'uv>=0.1.15'
      - name: "📦 Set up Python dependencies"
        id: install-deps
        run: |
          uv pip install --python $PYTHON -e "lenskit[test]"
        shell: bash
        env:
          PYTHON: '${{steps.install-python.outputs.python-path}}'
          UV_EXTRA_INDEX_URL: 'https://download.pytorch.org/whl/cpu'
          UV_INDEX_STRATEGY: unsafe-first-match
      - name: "🔍 Inspect environment"
        run: |
          python -m lenskit.util.envcheck
      - name: "🏃🏻‍➡️ Test LKPY"
        run: |
          python -m pytest --verbose --log-file=test.log --durations=25 --cov=lenskit/lenskit lenskit/tests
      - name: "📐 Coverage results"
        run: |
          coverage xml
          coverage report
          cp .coverage coverage.db
      - name: "📤 Upload test results"
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: 'test-vanilla-${{matrix.platform}}-py${{matrix.python}}'
          path: |
            test*.log
            coverage.db
            coverage.xml
  nojit:
    name: Non-JIT test coverage
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - name: "🛒 Checkout"
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: "🐍 Set up Python"
        id: install-python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - name: "🕶️ Set up uv"
        run: |
          pip install -U 'uv>=0.1.15'
      - name: "📦 Set up Python dependencies"
        id: install-deps
        run: |
          uv pip install --python $PYTHON -e "lenskit[test]" -e "lenskit-funksvd"
        shell: bash
        env:
          PYTHON: '${{steps.install-python.outputs.python-path}}'
          UV_EXTRA_INDEX_URL: 'https://download.pytorch.org/whl/cpu'
          UV_INDEX_STRATEGY: unsafe-first-match
      - name: "🔍 Inspect environment"
        run: |
          python -m lenskit.util.envcheck
      - name: "🏃🏻‍➡️ Test LKPY"
        run: |
          python -m pytest --verbose --log-file=test.log --durations=25 -m 'not slow' --cov=lenskit/lenskit --cov=lenskit-funksvd/lenskit lenskit/tests lenskit-funksvd/tests
        env:
          NUMBA_DISABLE_JIT: 1
          PYTORCH_JIT: 0
      - name: "📐 Coverage results"
        run: |
          coverage xml
          coverage report
          cp .coverage coverage.db
      - name: "📤 Upload test results"
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-nojit
          path: |
            test*.log
            coverage.db
            coverage.xml
  mindep:
    name: Minimal dependency tests
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - name: "🛒 Checkout"
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: "🐍 Set up Python"
        id: install-python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - name: "🕶️ Set up uv"
        run: |
          pip install -U 'uv>=0.1.15'
      - name: "📦 Set up Python dependencies"
        id: install-deps
        run: |
          uv pip install --python $PYTHON -e "lenskit[test]" --resolution=lowest-direct
        shell: bash
        env:
          PYTHON: '${{steps.install-python.outputs.python-path}}'
          UV_EXTRA_INDEX_URL: 'https://download.pytorch.org/whl/cpu'
          UV_INDEX_STRATEGY: unsafe-first-match
      - name: "🔍 Inspect environment"
        run: |
          python -m lenskit.util.envcheck
      - name: "🏃🏻‍➡️ Test LKPY"
        run: |
          python -m pytest --verbose --log-file=test.log --durations=25 --cov=lenskit/lenskit lenskit/tests
      - name: "📐 Coverage results"
        run: |
          coverage xml
          coverage report
          cp .coverage coverage.db
      - name: "📤 Upload test results"
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-mindep
          path: |
            test*.log
            coverage.db
            coverage.xml
  funksvd:
    name: 'FunkSVD tests on Python ${{matrix.python}}'
    runs-on: ubuntu-latest
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        python:
          - py311
          - py312
    steps:
      - name: "🛒 Checkout"
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: prefix-dev/setup-pixi@v0.8.1
        with:
          pixi-version: latest
          activate-environment: true
          environments: 'test-${{matrix.python}}-full'
          cache-write: false
      - name: "🔍 Inspect environment"
        run: |
          python -m lenskit.util.envcheck
      - name: "🏃🏻‍➡️ Test LKPY"
        run: |
          python -m pytest --verbose --log-file=test.log --durations=25 --cov=lenskit-funksvd/lenskit lenskit-funksvd/tests
      - name: "📐 Coverage results"
        run: |
          coverage xml
          coverage report
          cp .coverage coverage.db
      - name: "📤 Upload test results"
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: 'test-funksvd-py${{matrix.python}}'
          path: |
            test*.log
            coverage.db
            coverage.xml
  funksvd-mindep:
    name: Minimal dependency tests for FunkSVD
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - name: "🛒 Checkout"
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: "🐍 Set up Python"
        id: install-python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - name: "🕶️ Set up uv"
        run: |
          pip install -U 'uv>=0.1.15'
      - name: "📦 Set up Python dependencies"
        id: install-deps
        run: |
          uv pip install --python $PYTHON -e "lenskit[test]" -e "lenskit-funksvd" --resolution=lowest-direct
        shell: bash
        env:
          PYTHON: '${{steps.install-python.outputs.python-path}}'
          UV_EXTRA_INDEX_URL: 'https://download.pytorch.org/whl/cpu'
          UV_INDEX_STRATEGY: unsafe-first-match
      - name: "🔍 Inspect environment"
        run: |
          python -m lenskit.util.envcheck
      - name: "🏃🏻‍➡️ Test LKPY"
        run: |
          python -m pytest --verbose --log-file=test.log --durations=25 --cov=lenskit/lenskit --cov=lenskit-funksvd/lenskit lenskit/tests lenskit-funksvd/tests
      - name: "📐 Coverage results"
        run: |
          coverage xml
          coverage report
          cp .coverage coverage.db
      - name: "📤 Upload test results"
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-mindep-funksvd
          path: |
            test*.log
            coverage.db
            coverage.xml
  implicit:
    name: 'Implicit bridge tests on Python ${{matrix.python}}'
    runs-on: ubuntu-latest
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        python:
          - py311
          - py312
    steps:
      - name: "🛒 Checkout"
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: prefix-dev/setup-pixi@v0.8.1
        with:
          pixi-version: latest
          activate-environment: true
          environments: 'test-${{matrix.python}}-full'
          cache-write: false
      - name: "🔍 Inspect environment"
        run: |
          python -m lenskit.util.envcheck
      - name: "🏃🏻‍➡️ Test LKPY"
        run: |
          python -m pytest --verbose --log-file=test.log --durations=25 --cov=lenskit-implicit/lenskit lenskit-implicit/tests
      - name: "📐 Coverage results"
        run: |
          coverage xml
          coverage report
          cp .coverage coverage.db
      - name: "📤 Upload test results"
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: 'test-implicit-py${{matrix.python}}'
          path: |
            test*.log
            coverage.db
            coverage.xml
  implicit-mindep:
    name: Minimal dependency tests for Implicit
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - name: "🛒 Checkout"
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: "🐍 Set up Python"
        id: install-python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - name: "🕶️ Set up uv"
        run: |
          pip install -U 'uv>=0.1.15'
      - name: "📦 Set up Python dependencies"
        id: install-deps
        run: |
          uv pip install --python $PYTHON -e "lenskit[test]" -e "lenskit-implicit" --resolution=lowest-direct
        shell: bash
        env:
          PYTHON: '${{steps.install-python.outputs.python-path}}'
          UV_EXTRA_INDEX_URL: 'https://download.pytorch.org/whl/cpu'
          UV_INDEX_STRATEGY: unsafe-first-match
      - name: "🔍 Inspect environment"
        run: |
          python -m lenskit.util.envcheck
      - name: "🏃🏻‍➡️ Test LKPY"
        run: |
          python -m pytest --verbose --log-file=test.log --durations=25 --cov=lenskit/lenskit --cov=lenskit-implicit/lenskit lenskit/tests lenskit-implicit/tests
      - name: "📐 Coverage results"
        run: |
          coverage xml
          coverage report
          cp .coverage coverage.db
      - name: "📤 Upload test results"
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-mindep-implicit
          path: |
            test*.log
            coverage.db
            coverage.xml
  hpf:
    name: 'HPF bridge tests on Python ${{matrix.python}}'
    runs-on: ubuntu-latest
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        python:
          - py311
          - py312
    steps:
      - name: "🛒 Checkout"
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: prefix-dev/setup-pixi@v0.8.1
        with:
          pixi-version: latest
          activate-environment: true
          environments: 'test-${{matrix.python}}-full'
          cache-write: false
      - name: "🔍 Inspect environment"
        run: |
          python -m lenskit.util.envcheck
      - name: "🏃🏻‍➡️ Test LKPY"
        run: |
          python -m pytest --verbose --log-file=test.log --durations=25 --cov=lenskit-hpf/lenskit lenskit-hpf/tests
      - name: "📐 Coverage results"
        run: |
          coverage xml
          coverage report
          cp .coverage coverage.db
      - name: "📤 Upload test results"
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: 'test-hpf-py${{matrix.python}}'
          path: |
            test*.log
            coverage.db
            coverage.xml
  eval-tests:
    name: Evaluation-based tests
    runs-on: ubuntu-latest
    steps:
      - name: "🛒 Checkout"
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: prefix-dev/setup-pixi@v0.8.1
        with:
          pixi-version: latest
          activate-environment: true
          environments: test-py311-full
          cache-write: false
      - name: Cache ML data
        uses: actions/cache@v4
        with:
          path: |
            data
            !data/*.zip
          key: test-mldata-001-ml-100k-ml-20m
      - name: Download ML data
        run: |
          python -m lenskit.data.fetch ml-100k ml-20m
      - name: Run Eval Tests
        run: |
          python -m pytest --cov=lenskit/lenskit --cov=lenskit-funksvd/lenskit --cov=lenskit-implicit/lenskit --cov=lenskit-hpf/lenskit -m 'eval or realdata' --log-file test-eval.log */tests
      - name: "📐 Coverage results"
        run: |
          coverage xml
          coverage report
          cp .coverage coverage.db
      - name: "📤 Upload test results"
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-eval-tests
          path: |
            test*.log
            coverage.db
            coverage.xml
  doc-tests:
    name: 'Demos, examples, and docs'
    runs-on: ubuntu-latest
    steps:
      - name: "🛒 Checkout"
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: prefix-dev/setup-pixi@v0.8.1
        with:
          pixi-version: latest
          activate-environment: true
          environments: test-examples
          cache-write: false
      - name: Cache ML data
        uses: actions/cache@v4
        with:
          path: |
            data
            !data/*.zip
          key: test-mldata-001-ml-100k-ml-1m-ml-10m-ml-20m
      - name: Download ML data
        run: |
          python -m lenskit.data.fetch ml-100k ml-1m ml-10m ml-20m
      - name: "📕 Validate documentation examples"
        run: |
          python -m pytest --cov=lenskit/lenskit --cov=lenskit-funksvd/lenskit --cov=lenskit-implicit/lenskit --cov=lenskit-hpf/lenskit --nbval-lax --doctest-glob='*.rst' --log-file test-docs.log docs */lenskit
      - name: "📐 Coverage results"
        run: |
          coverage xml
          coverage report
          cp .coverage coverage.db
      - name: "📤 Upload test results"
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-examples
          path: |
            test*.log
            coverage.db
            coverage.xml
  results:
    name: Test suite results
    runs-on: ubuntu-latest
    needs:
      - conda
      - vanilla
      - nojit
      - mindep
      - funksvd
      - funksvd-mindep
      - implicit
      - implicit-mindep
      - hpf
      - eval-tests
      - doc-tests
    steps:
      - name: "🛒 Checkout"
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Add upstream remote & author config
        run: |
          git remote add upstream https://github.com/lenskit/lkpy.git
          git fetch upstream
          git config user.name "LensKit Bot"
          git config user.email lkbot@lenskit.org
      - name: "🧚 Set up Pixi"
        uses: prefix-dev/setup-pixi@v0.8.1
        with:
          pixi-version: latest
          activate-environment: true
          environments: report
      - name: "📥 Download test artifacts"
        uses: actions/download-artifact@v4
        with:
          pattern: test-*
          path: test-logs
      - name: "📋 List log files"
        run: ls -laR test-logs
      - name: "🔧 Fix coverage databases"
        run: |
          for dbf in test-logs/*/coverage.db; do
              echo "fixing $dbf"
              sqlite3 -echo "$dbf" "UPDATE file SET path = replace(path, '\', '/');"
          done
      - name: ⛙ Merge and report
        run: |
          coverage combine --keep test-logs/*/coverage.db
          coverage xml
          coverage html -d lenskit-coverage
          coverage report --format=markdown >coverage.md
      - name: Analyze diff coverage
        if: github.event_name == 'pull_request'
        run: |
          diff-cover --json-report diff-cover.json --markdown-report diff-cover.md \
              coverage.xml |tee diff-cover.txt
      - name: ± Measure and report coverage
        run: |
          echo $PR_NUMBER > ./lenskit-coverage/pr-number
          tclsh ./utils/measure-coverage.tcl
          cat lenskit-coverage/report.md >$GITHUB_STEP_SUMMARY
        env:
          PR_NUMBER: '${{ github.event.number }}'
          GH_TOKEN: '${{secrets.GITHUB_TOKEN}}'
      - name: "📤 Upload coverage report"
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: coverage-report
          path: lenskit-coverage/
      - name: "🚫 Fail if coverage is too low"
        run: coverage report --fail-under=90