diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml deleted file mode 100644 index 4ef58869c..000000000 --- a/.github/workflows/pre-release.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Pre-release checks - -on: - push: - branches: - - master - pull_request: - branches: - - master - -defaults: - run: - shell: bash - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - pre-release: - # Check pre-releases of dependencies on stable Python - runs-on: ${{ matrix.os }} - continue-on-error: true - strategy: - matrix: - os: ['ubuntu-latest'] - python-version: ['3.10', '3.11', '3.12'] - install: ['pip'] - check: ['tests'] - pip-flags: ['PRE_PIP_FLAGS'] - env: - INSTALL_TYPE: ${{ matrix.install }} - CHECK_TYPE: ${{ matrix.check }} - EXTRA_PIP_FLAGS: ${{ matrix.pip-flags }} - OS_TYPE: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - fetch-depth: 0 - - name: Install dependencies - run: .maint/ci/install_dependencies.sh - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Display Python version - run: python -c "import sys; print(sys.version)" - - name: Create virtual environment - run: .maint/ci/create_venv.sh - - name: Build archive - run: | - source .maint/ci/build_archive.sh - echo "ARCHIVE=$ARCHIVE" >> $GITHUB_ENV - - name: Install fMRIPrep - run: .maint/ci/install.sh - - name: Install extras - run: .maint/ci/install_extras.sh - - name: Run tests - run: .maint/ci/check.sh - - uses: codecov/codecov-action@v4 - with: - file: coverage.xml - token: ${{ secrets.CODECOV_TOKEN }} - if: ${{ always() }} diff --git a/.github/workflows/stable.yml b/.github/workflows/test.yml similarity index 60% rename from .github/workflows/stable.yml rename to .github/workflows/test.yml index b67aecaa4..53bcc4026 100644 --- a/.github/workflows/stable.yml +++ b/.github/workflows/test.yml @@ -16,6 +16,10 @@ defaults: run: shell: bash +# Force tox and pytest to use color +env: + FORCE_COLOR: true + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -24,47 +28,49 @@ permissions: contents: read jobs: - stable: + test: # Check each OS, all supported Python, minimum versions and latest releases runs-on: ${{ matrix.os }} strategy: matrix: os: ['ubuntu-latest'] python-version: ['3.10', '3.11', '3.12'] - install: ['pip'] - check: ['tests'] - pip-flags: [''] + dependencies: ['latest', 'pre'] + include: + - os: ubuntu-latest + python-version: '3.10' + dependencies: 'min' + env: - INSTALL_TYPE: ${{ matrix.install }} - CHECK_TYPE: ${{ matrix.check }} - EXTRA_PIP_FLAGS: ${{ matrix.pip-flags }} - OS_TYPE: ${{ matrix.os }} + DEPENDS: ${{ matrix.dependencies }} steps: - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 + - uses: actions/cache@v4 + with: + path: ~/.cache/templateflow + key: templateflow-v1 - name: Install dependencies - run: .maint/ci/install_dependencies.sh + run: | + sudo apt update + sudo apt install -y --no-install-recommends graphviz - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Display Python version run: python -c "import sys; print(sys.version)" - - name: Create virtual environment - run: .maint/ci/create_venv.sh - - name: Build archive + - name: Install tox run: | - source .maint/ci/build_archive.sh - echo "ARCHIVE=$ARCHIVE" >> $GITHUB_ENV - - name: Install fMRIPrep - run: .maint/ci/install.sh - - name: Install extras - run: .maint/ci/install_extras.sh - - name: Run tests - run: .maint/ci/check.sh + python -m pip install --upgrade pip + python -m pip install tox tox-gh-actions + - name: Show tox config + run: tox c + - name: Run tox + run: tox -v --exit-and-dump-after 1200 - uses: codecov/codecov-action@v4 with: file: coverage.xml diff --git a/.maint/ci/activate.sh b/.maint/ci/activate.sh deleted file mode 100644 index 567e13a67..000000000 --- a/.maint/ci/activate.sh +++ /dev/null @@ -1,9 +0,0 @@ -if [ -e virtenv/bin/activate ]; then - source virtenv/bin/activate -elif [ -e virtenv/Scripts/activate ]; then - source virtenv/Scripts/activate -else - echo Cannot activate virtual environment - ls -R virtenv - false -fi diff --git a/.maint/ci/build_archive.sh b/.maint/ci/build_archive.sh deleted file mode 100755 index 6e8be6fb9..000000000 --- a/.maint/ci/build_archive.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -echo "Building archive" - -source .maint/ci/activate.sh - -set -eu - -# Required dependencies -echo "INSTALL_TYPE = $INSTALL_TYPE" - -set -x - -if [ "$INSTALL_TYPE" = "sdist" -o "$INSTALL_TYPE" = "wheel" ]; then - python -m build -elif [ "$INSTALL_TYPE" = "archive" ]; then - ARCHIVE="/tmp/package.tar.gz" - git archive -o $ARCHIVE HEAD -fi - -if [ "$INSTALL_TYPE" = "sdist" ]; then - ARCHIVE=$( ls $PWD/dist/*.tar.gz ) -elif [ "$INSTALL_TYPE" = "wheel" ]; then - ARCHIVE=$( ls $PWD/dist/*.whl ) -elif [ "$INSTALL_TYPE" = "pip" ]; then - ARCHIVE="$PWD" -fi - -if [ "$INSTALL_TYPE" = "sdist" -o "$INSTALL_TYPE" = "wheel" ]; then - python -m pip install twine - python -m twine check $ARCHIVE -fi - -set +eux diff --git a/.maint/ci/check.sh b/.maint/ci/check.sh deleted file mode 100755 index d693e7086..000000000 --- a/.maint/ci/check.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -echo Running tests - -source .maint/ci/activate.sh - -set -eu - -# Required variables -echo CHECK_TYPE = $CHECK_TYPE - -set -x - -if [ "${CHECK_TYPE}" == "doc" ]; then - cd doc - make html && make doctest -elif [ "${CHECK_TYPE}" == "tests" ]; then - pytest --doctest-modules --cov fmriprep --cov-report xml \ - --junitxml=test-results.xml -v fmriprep -else - false -fi - -set +eux - -echo Done running tests diff --git a/.maint/ci/create_venv.sh b/.maint/ci/create_venv.sh deleted file mode 100755 index 6b6822e27..000000000 --- a/.maint/ci/create_venv.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -echo Creating isolated virtual environment - -source .maint/ci/env.sh - -set -eu - -# Required variables -echo SETUP_REQUIRES = $SETUP_REQUIRES - -set -x - -python -m pip install --upgrade pip virtualenv -virtualenv --python=python virtenv -source .maint/ci/activate.sh -python --version -python -m pip install -U $SETUP_REQUIRES -which python -which pip - -set +eux - -echo Done creating isolated virtual environment diff --git a/.maint/ci/env.sh b/.maint/ci/env.sh deleted file mode 100644 index 65e148b60..000000000 --- a/.maint/ci/env.sh +++ /dev/null @@ -1,6 +0,0 @@ -SETUP_REQUIRES="pip build" - -# Numpy and scipy upload nightly/weekly/intermittent wheels -NIGHTLY_WHEELS="https://pypi.anaconda.org/scipy-wheels-nightly/simple" -STAGING_WHEELS="https://pypi.anaconda.org/multibuild-wheels-staging/simple" -PRE_PIP_FLAGS="--pre --extra-index-url $NIGHTLY_WHEELS --extra-index-url $STAGING_WHEELS --prefer-binary" diff --git a/.maint/ci/install.sh b/.maint/ci/install.sh deleted file mode 100755 index e58e3fb15..000000000 --- a/.maint/ci/install.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -echo Installing fmriprep - -source .maint/ci/activate.sh -source .maint/ci/env.sh - -set -eu - -# Required variables -echo INSTALL_TYPE = $INSTALL_TYPE -echo CHECK_TYPE = $CHECK_TYPE -echo EXTRA_PIP_FLAGS = $EXTRA_PIP_FLAGS - -set -x - -if [ -n "$EXTRA_PIP_FLAGS" ]; then - EXTRA_PIP_FLAGS=${!EXTRA_PIP_FLAGS} -fi - -pip install $EXTRA_PIP_FLAGS $ARCHIVE - -# Basic import check -python -c 'import fmriprep; print(fmriprep.__version__)' - -if [ "$CHECK_TYPE" == "skiptests" ]; then - exit 0 -fi - -pip install $EXTRA_PIP_FLAGS "fmriprep[$CHECK_TYPE]" - -set +eux - -echo Done installing fmriprep diff --git a/.maint/ci/install_dependencies.sh b/.maint/ci/install_dependencies.sh deleted file mode 100755 index 7e0e33fe7..000000000 --- a/.maint/ci/install_dependencies.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -echo "Installing dependencies" - -set -eu - -# Required variables -echo OS_TYPE = $OS_TYPE - -if [ "$OS_TYPE" = "ubuntu-latest" ]; then - sudo apt update - sudo apt install -y graphviz -elif [ "$OS_TYPE" = "macos-latest" ]; then - brew install graphviz -else - echo "Unknown OS_TYPE: $OS_TYPE" -fi - -set +eux - -echo Done installing dependencies diff --git a/.maint/ci/install_extras.sh b/.maint/ci/install_extras.sh deleted file mode 100755 index 725adb65b..000000000 --- a/.maint/ci/install_extras.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -echo Installing dependencies - -source .maint/ci/activate.sh -source .maint/ci/env.sh - -set -eu - -# Required variables -echo EXTRA_PIP_FLAGS = $EXTRA_PIP_FLAGS -echo CHECK_TYPE = $CHECK_TYPE - -set -x - -if [ -n "$EXTRA_PIP_FLAGS" ]; then - EXTRA_PIP_FLAGS=${!EXTRA_PIP_FLAGS} -fi - -pip install $EXTRA_PIP_FLAGS "fmriprep[$CHECK_TYPE]" - -set +eux - -echo Done installing dependencies diff --git a/pyproject.toml b/pyproject.toml index 80412448d..cbace0737 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,10 +75,11 @@ telemetry = [ "sentry-sdk >= 1.3", ] test = [ - "coverage", + "coverage[toml]", "pytest", "pytest-cov", "pytest-env", + "pytest-xdist", ] maint = [ "fuzzywuzzy", @@ -127,11 +128,21 @@ exclude = ".*" [tool.pytest.ini_options] minversion = "6" -testpaths = ["fmriprep/tests"] +testpaths = ["fmriprep"] log_cli_level = "INFO" xfail_strict = true norecursedirs = [".git"] -addopts = ["-svx", "-ra", "--strict-config", "--strict-markers", "--doctest-modules"] +addopts = [ + "-svx", + "-ra", + "--strict-config", + "--strict-markers", + "--doctest-modules", + # Config pytest-cov + "--cov=fmriprep", + "--cov-report=xml", + "--cov-config=pyproject.toml", +] doctest_optionflags = "ALLOW_UNICODE NORMALIZE_WHITESPACE ELLIPSIS" env = "PYTHONHASHSEED=0" filterwarnings = ["ignore::DeprecationWarning"] @@ -181,3 +192,15 @@ inline-quotes = "single" [tool.ruff.format] quote-style = "single" + +[tool.coverage.run] +branch = true +omit = [ + "*/_version.py" +] + +[tool.coverage.paths] +source = [ + "fmriprep", + "**/site-packages/fmriprep" +] diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..7b9fb6821 --- /dev/null +++ b/tox.ini @@ -0,0 +1,113 @@ +[tox] +requires = + tox>=4 +envlist = + py3{10,11,12}-latest + py310-min + py3{10,11,12}-pre +skip_missing_interpreters = true + +# Configuration that allows us to split tests across GitHub runners effectively +[gh-actions] +python = + 3.10: py310 + 3.11: py311 + 3.12: py312 + +[gh-actions:env] +DEPENDS = + min: min + latest: latest + pre: pre + +[testenv] +description = Pytest with coverage +labels = test +pip_pre = + pre: true +pass_env = + # getpass.getuser() sources for Windows: + LOGNAME + USER + LNAME + USERNAME + # Pass user color preferences through + PY_COLORS + FORCE_COLOR + NO_COLOR + CLICOLOR + CLICOLOR_FORCE + PYTHON_GIL +extras = test +setenv = + pre: PIP_EXTRA_INDEX_URL=https://pypi.anaconda.org/scientific-python-nightly-wheels/simple +deps = + min: nibabel == 4.0.1 + min: nipype == 1.8.5 + min: nitransforms == 21.0.0 + min: numpy == 1.22 + min: psutil == 5.4 + min: pybids == 0.15.2 + min: tedana == 23.0.2 + min: templateflow == 24.1.0 + +commands_pre = + python scripts/fetch_templates.py +commands = + pytest --cov-report term-missing --durations=20 --durations-min=1.0 {posargs:-n auto} + +[testenv:style] +description = Check our style guide +labels = check +deps = + ruff +skip_install = true +commands = + ruff check --diff + ruff format --diff + +[testenv:style-fix] +description = Auto-apply style guide to the extent possible +labels = pre-release +deps = + ruff +skip_install = true +commands = + ruff check --fix + ruff format + ruff check --select ISC001 + +[testenv:spellcheck] +description = Check spelling +labels = check +deps = + codespell[toml] +skip_install = true +commands = + codespell . {posargs} + +[testenv:build{,-strict}] +labels = + check + pre-release +deps = + build + twine +skip_install = true +set_env = + # Ignore specific known warnings: + # https://github.com/pypa/pip/issues/11684 + # https://github.com/pypa/pip/issues/12243 + strict: PYTHONWARNINGS=error,once:pkg_resources is deprecated as an API.:DeprecationWarning:pip._internal.metadata.importlib._envs,once:Unimplemented abstract methods {'locate_file'}:DeprecationWarning:pip._internal.metadata.importlib._dists +commands = + python -m build + python -m twine check dist/* + +[testenv:publish] +depends = build +labels = release +deps = + twine +skip_install = true +commands = + python -m twine upload dist/*