Skip to content

Add FX Borzoi score cache #329

Add FX Borzoi score cache

Add FX Borzoi score cache #329

Workflow file for this run

# Continuous integration for GenoLeWM.
#
# Jobs:
# lint — ruff format check, ruff lint
# types — mypy --strict
# tests — pytest matrix (Python × OS), coverage upload
# ml-smoke — fast fixture-backed ML smoke suite
# eval-smoke — generated fixture eval regression gate
# gates — custom AST linters + public-API snapshot
# build — package build smoke test (sdist + wheel)
# docs — mkdocs strict build
# ci-required — required-check fan-in
#
# All jobs run on every PR and on pushes to main. The matrix is
# intentionally lean on PRs (Linux only by default; macOS/Windows
# included via matrix.include) and full on main / releases.
name: CI
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
env:
PYTHONDONTWRITEBYTECODE: "1"
PYTHONUNBUFFERED: "1"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
FORCE_COLOR: "1"
jobs:
lint:
name: Lint (ruff)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml
- name: Install
run: python -m pip install -e ".[dev]"
- name: ruff format --check
run: ruff format --check geno_lewm tools tests
- name: ruff check
run: ruff check geno_lewm tools tests --output-format=github
types:
name: Types (mypy --strict)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml
- name: Install
run: python -m pip install -e ".[dev]"
- name: mypy
run: mypy geno_lewm tools
gates:
name: Contract gates (errors / events / surface / net / print / spdx)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml
- name: Install
run: python -m pip install -e ".[dev]"
- name: check_error_codes
run: python -m tools.lint.check_error_codes
- name: check_event_names
run: python -m tools.lint.check_event_names
- name: check_no_print
run: python -m tools.lint.check_no_print
- name: check_network_confined
run: python -m tools.lint.check_network_confined
- name: check_scope_language (project scope)
run: python -m tools.lint.check_scope_language
- name: check first-experiment dataset spec
run: >-
python -m tools.release.dataset_snapshot
--spec-json configs/first_experiment/dataset-snapshot-snv.json
--check-spec
- name: check_license_headers (SPDX)
run: python -m tools.lint.check_license_headers
- name: public-API snapshot
run: python -m tools.api.snapshot check
tests:
name: Tests (Python ${{ matrix.python }} on ${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python: ["3.10", "3.11", "3.12", "3.13"]
include:
- os: macos-latest
python: "3.12"
- os: windows-latest
python: "3.12"
steps:
- uses: actions/checkout@v6
with:
# Fetch enough history for the changed-files coverage gate to
# diff against the PR base branch (`origin/<base_ref>`).
fetch-depth: 0
- name: Set up Python
if: runner.os != 'Windows'
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python }}
cache: pip
cache-dependency-path: pyproject.toml
- name: Set up Python (Windows)
if: runner.os == 'Windows'
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python }}
- name: Install
# Install the torch-backed extras on the canonical coverage combo
# (ubuntu-latest / 3.12) so ML paths are exercised and counted by the
# changed-files coverage gate; keep the other matrix legs lean.
run: |
if [ "${{ matrix.os }}" = "ubuntu-latest" ] && [ "${{ matrix.python }}" = "3.12" ]; then
python -m pip install -e ".[dev,train,eval]"
else
python -m pip install -e ".[dev]"
fi
shell: bash
- name: pytest
# xdist on Windows uses spawn-based workers that re-import the
# package per worker; combined with module-level state in
# observability/metrics, this surfaces flaky failures on Windows
# specifically. Run serially on Windows, parallel elsewhere.
run: |
if [ "${{ runner.os }}" = "Windows" ]; then
pytest --cov=geno_lewm --cov-branch --cov-report=xml --cov-report=term-missing -v --tb=long 2>&1 | tee pytest.out
else
pytest --cov=geno_lewm --cov-branch --cov-report=xml --cov-report=term-missing -n auto -v --tb=long 2>&1 | tee pytest.out
fi
shell: bash
- name: Upload pytest output (on failure)
if: failure()
uses: actions/upload-artifact@v7
with:
name: pytest-${{ matrix.os }}-${{ matrix.python }}
path: pytest.out
retention-days: 7
if-no-files-found: ignore
- name: Upload coverage to Codecov
if: matrix.os == 'ubuntu-latest' && matrix.python == '3.12'
uses: codecov/codecov-action@v7
with:
files: ./coverage.xml
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
- name: Changed-files coverage gate (PRs only)
# Runs on the canonical matrix combo only; one signal is enough.
# Skipped on pushes to main / tags because there is no PR base to diff against.
if: >-
github.event_name == 'pull_request' &&
matrix.os == 'ubuntu-latest' &&
matrix.python == '3.12'
run: |
git fetch --no-tags --depth=50 origin "${{ github.base_ref }}"
python -m tools.ci.coverage_gate \
--coverage-xml coverage.xml \
--base "origin/${{ github.base_ref }}" \
--threshold 0.83
shell: bash
ml-smoke:
name: ML smoke (fixture-backed)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml
- name: Install
run: python -m pip install -e ".[dev]"
- name: pytest tests/ml
run: pytest tests/ml -q --tb=long --durations=10
eval-smoke:
name: Eval smoke (fixture-backed)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml
- name: Install
run: python -m pip install -e ".[dev]"
- name: Run eval smoke gate
run: >-
python -m tools.ci.eval_smoke_gate
--work-dir .eval-smoke
--summary-json .eval-smoke/eval_smoke_summary.json
- name: Upload eval smoke summary
uses: actions/upload-artifact@v4
with:
name: eval-smoke-summary
path: .eval-smoke/eval_smoke_summary.json
retention-days: 14
build:
name: Build (sdist + wheel)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml
- name: Install build deps
run: python -m pip install build twine
- name: Build
run: python -m build
- name: Twine check
run: twine check dist/*
- name: Check sdist release assets
run: python -m tools.release.check_sdist_assets dist/*.tar.gz
- name: Smoke install
run: |
python -m pip install dist/*.whl
python -c "import geno_lewm; print(geno_lewm.__version__)"
python -c "from geno_lewm import EditSpec, apply_edit; print(EditSpec(chrom='1', pos=1, ref='A', alt='T'))"
- name: Upload dist artifacts
uses: actions/upload-artifact@v7
with:
name: dist
path: dist/
retention-days: 14
docs:
name: Docs (mkdocs --strict)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml
- name: Install
run: python -m pip install -e ".[docs]"
- name: Build docs (strict)
run: mkdocs build --strict
- name: Upload site artifact
uses: actions/upload-artifact@v7
with:
name: docs-site
path: site/
retention-days: 14
# Single required check that fans-in all the matrix jobs above.
# Branch protection just needs to require this one.
ci-required:
name: CI required
if: always()
needs: [lint, types, gates, tests, ml-smoke, eval-smoke, build, docs]
runs-on: ubuntu-latest
steps:
- name: Check jobs
if: >-
needs.lint.result != 'success' ||
needs.types.result != 'success' ||
needs.gates.result != 'success' ||
needs.tests.result != 'success' ||
needs.ml-smoke.result != 'success' ||
needs.eval-smoke.result != 'success' ||
needs.build.result != 'success' ||
needs.docs.result != 'success'
run: |
echo "One or more required CI jobs failed; see job logs above."
exit 1
- run: echo "All required CI jobs passed."