diff --git a/.coverage b/.coverage index 54da429..39a2087 100644 Binary files a/.coverage and b/.coverage differ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 329730f..962e883 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,17 +10,23 @@ concurrency: group: ci-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +# Optional: least-privilege default +permissions: + contents: read + jobs: precommit: name: pre-commit (lint/format/tests) runs-on: ubuntu-latest - if: github.event_name != 'push' || github.ref == 'refs/heads/main' + # Run on PRs, and on pushes to main + if: ${{ github.event_name != 'push' || github.ref == 'refs/heads/main' }} steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Python + id: py uses: actions/setup-python@v5 with: python-version: "3.12" @@ -37,33 +43,33 @@ jobs: uses: actions/cache@v4 with: path: .venv - key: venv-${{ runner.os }}-${{ steps.py.outputs.python-version || '3.12' }}-${{ hashFiles('poetry.lock') }} + key: venv-${{ runner.os }}-${{ steps.py.outputs.python-version }}-${{ hashFiles('poetry.lock') }} restore-keys: | venv-${{ runner.os }}- - name: Install dependencies run: poetry install --no-interaction - # This runs all hooks defined in pre-commit (e.g., ruff, pytest, mypy) + # Runs all hooks defined in pre-commit (e.g., ruff, pytest, mypy) - name: Run pre-commit on all files run: | poetry run pre-commit install poetry run pre-commit run --all-files - # === Optional: separate pytest job (keep if you want tests isolated) === + # Optional: keep if you want tests isolated from pre-commit tests: name: pytest (separated) runs-on: ubuntu-latest needs: precommit - if: > - github.event_name != 'push' || github.ref == 'refs/heads/main' - # If you already run pytest in pre-commit, you can disable this whole job + # Run on PRs, and on pushes to main + if: ${{ github.event_name != 'push' || github.ref == 'refs/heads/main' }} steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Python + id: py uses: actions/setup-python@v5 with: python-version: "3.12" @@ -80,7 +86,7 @@ jobs: uses: actions/cache@v4 with: path: .venv - key: venv-${{ runner.os }}-${{ steps.py.outputs.python-version || '3.12' }}-${{ hashFiles('poetry.lock') }} + key: venv-${{ runner.os }}-${{ steps.py.outputs.python-version }}-${{ hashFiles('poetry.lock') }} restore-keys: | venv-${{ runner.os }}- @@ -104,4 +110,3 @@ jobs: with: name: pytest-junit path: pytest-junit.xml - if-no-files-found: ignore diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2bdc951..7a60c74 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,31 +1,31 @@ +# .pre-commit-config.yaml repos: - # Ruff - linting and formatting - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.13.0 hooks: - - id: ruff - args: [--fix] # automatically fix issues - - id: ruff-format # format code + - id: ruff # lint + - id: ruff-format # format - # mypy - static type checking - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.18.1 hooks: - id: mypy - # Built-in file checks - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - - id: trailing-whitespace # remove trailing spaces - - id: end-of-file-fixer # ensure files end with newline + - id: trailing-whitespace + - id: end-of-file-fixer - # pytest with 100% coverage requirement + # Local hook for pytest with 100% coverage requirement - repo: local hooks: - id: pytest-coverage name: pytest with 100% coverage - entry: poetry run pytest --cov=src --cov-fail-under=100 --cov-report=term-missing language: system + entry: poetry run pytest -q --maxfail=1 --disable-warnings --cov-fail-under=100 --cov-report=term-missing pass_filenames: false always_run: true + env: + PYTHONDONTWRITEBYTECODE: "1" # stop __pycache__ / *.pyc + COVERAGE_FILE: ".git/.coverage" # keep coverage DB inside .git diff --git a/tests/perf/test_averages_perf.py b/tests/perf/test_averages_perf.py index 2e9120e..ef2f071 100644 --- a/tests/perf/test_averages_perf.py +++ b/tests/perf/test_averages_perf.py @@ -17,7 +17,7 @@ def _best_time(fn, data, repeats=5): return min(times), statistics.mean(times) -EXPECTED_MAX_TIME = 0.055 # 55 milliseconds +EXPECTED_MAX_TIME = 0.053 # 53 milliseconds def test_average_age_performance(capfd):