Skip to content

feat(deps): upgrade transformers to 5.x and sentence-transformers to 5.2+ #137

feat(deps): upgrade transformers to 5.x and sentence-transformers to 5.2+

feat(deps): upgrade transformers to 5.x and sentence-transformers to 5.2+ #137

Workflow file for this run

name: CI
on:
push:
branches:
- dev
pull_request:
# `labeled` is required so adding the `full-ci` label re-triggers CI
# with the full OS/Python matrix on an open PR.
types: [opened, synchronize, reopened, labeled]
# Manual coverage run. Never fired by push/PR, so coverage stays off the
# per-commit hot path; on this event the test jobs toggle `--cov` and the
# `coverage-report` job combines and publishes the total.
workflow_dispatch:
inputs:
coverage:
description: 'Measure test coverage and publish a combined report'
type: boolean
default: false
python-version:
description: 'Python version to measure coverage on'
type: string
default: '3.12'
concurrency:
# Include the event name so a manual coverage dispatch and a push on the
# same ref land in separate groups instead of cancelling each other.
group: ci-${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
jobs:
# Decide matrix scope once and fan out to every downstream job.
# Full matrix (all OSes + all Python versions) runs on push to dev and
# on PRs carrying the `full-ci` label. Default PR commits only run
# ubuntu-latest + Python 3.14 to keep per-commit cost low.
setup:
name: Setup
runs-on: ubuntu-latest
# The `labeled` trigger fires for *any* label, but only `full-ci` should
# do work. Skip the whole run when some other label is added, so stray
# labels don't burn a redundant CI run. Every non-`labeled` event (push,
# opened/synchronize/reopened) proceeds because github.event.action is
# then not 'labeled'. When this is false the run no-ops and `all-tests`
# still reports success (see its guarded step below).
if: github.event.action != 'labeled' || github.event.label.name == 'full-ci'
outputs:
matrix: ${{ steps.compute.outputs.matrix }}
warm_os: ${{ steps.compute.outputs.warm_os }}
full: ${{ steps.compute.outputs.full }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Compute matrix
id: compute
env:
EVENT_NAME: ${{ github.event_name }}
LABELS_JSON: ${{ toJSON(github.event.pull_request.labels.*.name) }}
DISPATCH_PYTHON: ${{ inputs.python-version }}
run: python .ci/compute_matrix.py
warm-cache:
name: Warm HF cache
needs: setup
strategy:
fail-fast: false
# Per-OS runners are needed because actions/cache pins by ${{ runner.os }}
# so Linux test jobs only restore caches saved by a Linux runner (likewise
# for Windows). The prewarm config itself is shared (.ci/hf-prewarm.yaml).
matrix:
os: ${{ fromJSON(needs.setup.outputs.warm_os) }}
runs-on: ${{ matrix.os }}
# Best-effort: an HF outage here must not cancel every PR. Test jobs
# `needs: warm-cache` but proceed regardless via continue-on-error.
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Cache Hugging Face
id: cache-hf
uses: actions/cache@v5
with:
path: ~/.cache/huggingface
# Stable key (no github.run_id): when the prewarm config and the
# SHA pins are unchanged from a previous run, the cache restores
# with cache-hit=true and the warm script is skipped entirely.
# Bumping a pinned SHA invalidates the cache automatically since
# hashFiles includes _pinned_revisions.py.
key: ${{ runner.os }}-hf-warmed-${{ hashFiles('.ci/hf-prewarm.yaml', 'src/autointent/configs/_pinned_revisions.py') }}
- name: Install uv
if: steps.cache-hf.outputs.cache-hit != 'true'
uses: astral-sh/setup-uv@v7
with:
version: "0.10.0"
- name: Run warm-cache script
if: steps.cache-hf.outputs.cache-hit != 'true'
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
# --no-project: skip the autointent install. The warm script loads
# _pinned_revisions.py via importlib.util.spec_from_file_location
# specifically so it doesn't need autointent on sys.path; without
# --no-project, `uv run` syncs the full project dep tree (~83
# packages, ~20s) before running.
run: uv run --no-project --with 'huggingface_hub[hf_xet]' --with pyyaml --with datasets python .ci/warm_hf_cache.py --config .ci/hf-prewarm.yaml
unit-tests:
name: unit-tests
needs: [setup, warm-cache]
uses: ./.github/workflows/reusable-test.yaml
secrets: inherit
with:
test_command: pytest -n auto --ignore=tests/modules/scoring/ --ignore=tests/pipeline --ignore=tests/embedder
# wandb + codecarbon so the real WandbCallback / EmissionsTrackerCallback
# tests in tests/callback/ run here instead of being skipped.
extras: --extra openai --extra wandb --extra codecarbon
matrix: ${{ needs.setup.outputs.matrix }}
coverage_artifact: ${{ inputs.coverage && 'unit' || '' }}
test-embedder:
name: test-embedder
needs: [setup, warm-cache]
uses: ./.github/workflows/reusable-test.yaml
secrets: inherit
with:
# --extra openai so the real OpenaiEmbeddingBackend tests (respx-mocked,
# importorskip'd) actually run here instead of being skipped.
test_command: pytest -n auto tests/embedder/
extras: --extra sentence-transformers --extra transformers --extra openai
matrix: ${{ needs.setup.outputs.matrix }}
coverage_artifact: ${{ inputs.coverage && 'embedder' || '' }}
test-scorers:
name: test-scorers
needs: [setup, warm-cache]
strategy:
fail-fast: false
matrix:
dependency-group: [ "base", "transformers", "peft", "catboost" ]
uses: ./.github/workflows/reusable-test.yaml
secrets: inherit
with:
test_command: pytest -n auto tests/modules/scoring/
extras: ${{ matrix.dependency-group != 'base' && format('--extra {0}', matrix.dependency-group) || '' }}
matrix: ${{ needs.setup.outputs.matrix }}
coverage_artifact: ${{ inputs.coverage && format('scorers-{0}', matrix.dependency-group) || '' }}
test-presets:
name: test-presets
needs: [setup, warm-cache]
uses: ./.github/workflows/reusable-test.yaml
secrets: inherit
with:
test_command: pytest -n auto tests/pipeline/test_presets.py
extras: --extra catboost --extra peft --extra transformers --extra sentence-transformers
matrix: ${{ needs.setup.outputs.matrix }}
coverage_artifact: ${{ inputs.coverage && 'presets' || '' }}
test-optimization:
name: test-optimization
needs: [setup, warm-cache]
uses: ./.github/workflows/reusable-test.yaml
secrets: inherit
with:
test_command: pytest -n auto tests/pipeline/test_optimization.py tests/pipeline/test_pipeline_interruption.py tests/pipeline/test_validation.py
extras: --extra catboost --extra peft --extra transformers --extra sentence-transformers
matrix: ${{ needs.setup.outputs.matrix }}
coverage_artifact: ${{ inputs.coverage && 'optimization' || '' }}
test-inference:
name: test-inference
needs: [setup, warm-cache]
uses: ./.github/workflows/reusable-test.yaml
secrets: inherit
with:
test_command: pytest -n auto tests/pipeline/test_inference.py
extras: --extra catboost --extra peft --extra transformers --extra sentence-transformers
matrix: ${{ needs.setup.outputs.matrix }}
coverage_artifact: ${{ inputs.coverage && 'inference' || '' }}
# Single aggregator over every test fan-out so the GitHub ruleset only
# needs one required check ("all-tests") instead of 54 matrix entries.
# needs.<job>.result for a matrix caller is already the aggregate, so
# six needs cover all combinations. `if: always()` makes this job run
# even when an upstream failed, and the inner step fails on
# failure/cancelled/skipped — so a skipped required check (which most
# rulesets treat as not-passing) can't slip through.
all-tests:
name: all-tests
if: always()
needs:
- unit-tests
- test-embedder
- test-presets
- test-optimization
- test-inference
- test-scorers
runs-on: ubuntu-latest
steps:
- name: Check that every test job succeeded
# First clause mirrors `setup.if`: a non-`full-ci` `labeled` event
# skips the whole matrix on purpose, so those skips are not a
# failure and `all-tests` stays green for the unchanged SHA. Every
# other event keeps the strict failure/cancelled/skipped gate.
if: >-
(github.event.action != 'labeled' || github.event.label.name == 'full-ci')
&& (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped'))
run: |
echo "One or more test jobs did not succeed:"
echo '${{ toJson(needs) }}'
exit 1
# Combine the per-group `.coverage.<group>` artifacts uploaded by the test
# jobs into a single report. Only runs on the manual coverage dispatch.
# `always()` lets it still combine the groups that succeeded if one fails,
# so a single flaky group doesn't sink the whole measurement.
coverage-report:
name: coverage-report
if: always() && github.event_name == 'workflow_dispatch' && inputs.coverage
needs:
- unit-tests
- test-embedder
- test-presets
- test-optimization
- test-inference
- test-scorers
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
version: "0.10.0"
# merge-multiple flattens every `coverage-data-*` artifact into the
# workspace root so the `.coverage.<group>` files sit next to pyproject
# for `coverage combine`.
- name: Download coverage data
uses: actions/download-artifact@v8
with:
pattern: coverage-data-*
merge-multiple: true
- name: Combine and report coverage
run: uv run --no-project --with 'coverage[toml]>=7.6,<8' python .ci/coverage_report.py
- name: Upload coverage report
uses: actions/upload-artifact@v7
with:
name: coverage-report
path: |
coverage.xml
htmlcov/
if-no-files-found: error