name: Tests

on:
  push:
    branches: [ master ]
    tags: [ '[0-9]+.[0-9]+.[0-9]+*' ]
  pull_request:
    branches: [ master ]

env:
  CIBW_TEST_COMMAND: python -m unittest discover --start-directory {project}
  CIBW_SKIP: pp* cp38-* cp39-* cp310-*
  CIBW_ENVIRONMENT_PASS_LINUX: TOMLI_USE_MYPYC

jobs:

  linters:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-python@v5
      with:
        python-version: '3.11'

    - name: Install pre-commit
      run: |
        pip install pre-commit

    - name: run linters
      # pre-commit also runs in pre-commit.ci, but let's have it here too
      # to block `pypi-publish` job from triggering if pre-commit fails
      run: |
        pre-commit run --all-files

  tests:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        python-version: ['pypy3.10', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14-dev']
        os: [ubuntu-latest, macos-latest, windows-latest]
    continue-on-error: ${{ matrix.python-version == '3.14-dev' }}

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}

    - name: Install package
      run: |
        pip install .

    - name: Test with unittest
      run: |
        python -m unittest

  coverage:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-python@v5
      with:
        python-version: '3.x'
    - name: Install package and coverage deps
      run: |
        pip install . coverage
    - name: Measure coverage
      run: |
        # Coverage should be 100% without external tests
        rm -rf tests/data/valid/_external/
        rm -rf tests/data/invalid/_external/
        coverage run -m unittest
        coverage report --fail-under=100
    - name: Report coverage
      uses: codecov/codecov-action@v4
      with:
        token: ${{ secrets.CODECOV_TOKEN }}

  binary-wheels-standard:
    name: Binary wheels for ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]

    steps:
    - uses: actions/checkout@v4

    - uses: actions/setup-python@v5
      with:
        python-version: '3.x'

    - name: Switch build backend to setuptools
      run: |
        pip install -r scripts/requirements.txt
        python scripts/use_setuptools.py

    - name: Build wheels
      uses: pypa/cibuildwheel@v2.22.0
      env:
        CIBW_ARCHS_MACOS: x86_64 arm64
        TOMLI_USE_MYPYC: '1'

    - uses: actions/upload-artifact@v4
      with:
        name: artifact-standard-${{ matrix.os }}
        path: wheelhouse/*.whl
        if-no-files-found: error

  pure-python-wheel-and-sdist:
    name: Build a pure Python wheel and source distribution
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4

    - name: Install build dependencies
      run: pip install build

    - name: Build
      run: python -m build

    - uses: actions/upload-artifact@v4
      with:
        name: artifact-pure-python
        path: dist/*
        if-no-files-found: error

  binary-wheels-arm:
    name: Build Linux wheels for ARM
    runs-on: ubuntu-latest
    # Very slow (~ 1 hour), no need to run on PRs
    if: >
      github.event_name == 'push'
      &&
      (github.ref == 'refs/heads/master' || startsWith(github.event.ref, 'refs/tags'))
    steps:
    - uses: actions/checkout@v4

    - uses: actions/setup-python@v5
      with:
        python-version: '3.x'

    - name: Switch build backend to setuptools
      run: |
        pip install -r scripts/requirements.txt
        python scripts/use_setuptools.py

    - name: Set up QEMU
      uses: docker/setup-qemu-action@v3
      with:
        platforms: arm64

    - name: Build wheels
      uses: pypa/cibuildwheel@v2.22.0
      env:
        CIBW_ARCHS_LINUX: aarch64
        TOMLI_USE_MYPYC: '1'

    - uses: actions/upload-artifact@v4
      with:
        name: artifact-arm-linux
        path: wheelhouse/*.whl
        if-no-files-found: error

  allgood:
    runs-on: ubuntu-latest
    needs:
    - tests
    - coverage
    - linters
    - binary-wheels-standard
    - pure-python-wheel-and-sdist
    - binary-wheels-arm
    steps:
    - run: echo "Great success!"

  pypi-publish:
    # Only publish if all other jobs succeed
    needs: [ allgood ]
    if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
    runs-on: ubuntu-latest
    steps:
    - uses: actions/download-artifact@v4
      with:
        path: dist
        pattern: artifact-*
        merge-multiple: true
    - uses: actions/setup-python@v5
      with:
        python-version: '3.x'
    - name: Install twine
      run: |
        pip install twine
    - name: Check and publish
      run: |
        twine check --strict dist/*
        twine upload dist/*
      env:
        TWINE_USERNAME: __token__
        TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
