# By default, for PRs CircleCI will build only examples that have changed.
# For main commits, builds are skipped entirely, as we only do full builds
# scheduled for one time daily.
#
# Tagging a commit with the following overrides these behaviors:
# - [circle front] will run the front page examples and perform test-doc
# - [circle full] will run all examples and perform test-doc
# - [circle linkcheck] will run our linkcheck job
# - [circle deploy] on a main or maint/* commit will try to immediately build
# and deploy docs rather than waiting for the nightly build
version: 2.1
_check_skip: &check_skip
name: Check-skip
command: |
set -e
export COMMIT_MESSAGE=$(git log --format=oneline -n 1);
if [[ "$CIRCLE_PULL_REQUEST" != "" ]] && ([[ "$COMMIT_MESSAGE" == *"[skip circle]"* ]] || [[ "$COMMIT_MESSAGE" == *"[circle skip]"* ]]); then
echo "Skip detected, exiting job ${CIRCLE_JOB} for PR ${CIRCLE_PULL_REQUEST}."
circleci-agent step halt;
fi
jobs:
build_docs:
parameters:
scheduled:
type: string
default: "false"
machine:
image: ubuntu-2404:current
# large 4 vCPUs 15GB mem
# https://discuss.circleci.com/t/changes-to-remote-docker-reporting-pricing/47759
resource_class: large
steps:
- restore_cache:
keys:
- source-cache
- checkout
- run:
name: Complete checkout
command: |
set -e
if ! git remote -v | grep upstream; then
git remote add upstream https://github.com/mne-tools/mne-python.git
fi
git remote set-url upstream https://github.com/mne-tools/mne-python.git
git fetch upstream
- save_cache:
key: source-cache
paths:
- ".git"
- run:
<<: *check_skip
- run:
name: Merge with upstream and triage run
command: |
set -e
echo $(git log -1 --pretty=%B) | tee gitlog.txt
echo ${CI_PULL_REQUEST//*pull\//} | tee merge.txt
if [[ $(cat merge.txt) != "" ]]; then
echo "Merging $(cat merge.txt)";
git pull --ff-only upstream "refs/pull/$(cat merge.txt)/merge";
else
if [[ "$CIRCLE_BRANCH" == "main" ]]; then
KIND=dev
else
KIND=stable
fi
export COMMIT_MESSAGE=$(git log --format=oneline -n 1);
if [[ "<< parameters.scheduled >>" == "true" ]]; then
echo "Scheduled full build detected, checking if it's required."
wget https://mne.tools/${KIND}/_version.txt;
REMOTE_VERSION=$(cat _version.txt)
THIS_VERSION=$(git rev-parse HEAD)
echo "Current ${KIND} SHA: ${REMOTE_VERSION}"
echo "This ${KIND} SHA: ${THIS_VERSION}"
if [[ "${THIS_VERSION}" != "${REMOTE_VERSION}" ]]; then
echo "Rebuild required."
else
echo "Rebuild skipped."
circleci-agent step halt;
fi
elif [[ "$COMMIT_MESSAGE" == *"[circle deploy]"* ]]; then
echo "Forced deployed build detected, building and deploying docs";
else
echo "Waiting until scheduled run to build ${KIND} docs, exiting job ${CIRCLE_JOB}."
circleci-agent step halt;
fi
fi
- run:
name: Set BASH_ENV
command: ./tools/circleci_bash_env.sh
- run:
name: check neuromag2ft
command: |
neuromag2ft --version
- run:
name: Install fonts needed for diagrams
command: |
mkdir -p $HOME/.fonts
echo "Source Code Pro"
curl https://codeload.github.com/adobe-fonts/source-code-pro/tar.gz/2.038R-ro/1.058R-it/1.018R-VAR | tar xz -C $HOME/.fonts
echo "Source Sans Pro"
curl https://codeload.github.com/adobe-fonts/source-sans/tar.gz/3.028R | tar xz -C $HOME/.fonts
fc-cache -f
# Load pip cache
- restore_cache:
keys:
- pip-cache-0
- restore_cache:
keys:
- user-install-bin-cache-310
# Hack in uninstalls of libraries as necessary if pip doesn't do the right thing in upgrading for us...
- run:
name: Get Python running
command: |
./tools/circleci_dependencies.sh
- save_cache:
key: pip-cache-0
paths:
- ~/.cache/pip
- save_cache:
key: user-install-bin-cache-310
paths:
- ~/.local/lib/python3.10/site-packages
- ~/.local/bin
- run:
name: Check Qt
command: |
./tools/check_qt_import.sh PyQt6
# Load tiny cache so that ~/.mne does not need to be created below
- restore_cache:
keys:
- data-cache-tiny-0
# Look at what we have and fail early if there is some library conflict
- run:
name: Check installation
command: |
which python
QT_DEBUG_PLUGINS=1 mne sys_info -pd
python -c "import numpy; numpy.show_config()"
python -c "import dipy.align.metrics"
LIBGL_DEBUG=verbose python -c "import pyvistaqt; pyvistaqt.BackgroundPlotter(show=True)"
python -c "import mne; mne.set_config('MNE_USE_CUDA', 'false')" # this is needed for the config tutorial
python -c "import mne; mne.set_config('MNE_LOGGING_LEVEL', 'info')"
python -c "import mne; level = mne.get_config('MNE_LOGGING_LEVEL'); assert level.lower() == 'info', repr(level)"
- run:
name: List packages
command: python -m pip list
# Figure out if we should run a full build or specify a pattern
- restore_cache:
keys:
- data-cache-tiny-1
- restore_cache:
keys:
- data-cache-multimodal
- restore_cache:
keys:
- data-cache-limo
- restore_cache:
keys:
- data-cache-fsaverage
- restore_cache:
keys:
- data-cache-bst-raw
- restore_cache:
keys:
- data-cache-bst-phantom-ctf
- restore_cache:
keys:
- data-cache-bst-phantom-elekta
- restore_cache:
keys:
- data-cache-bst-phantom-kernel
- restore_cache:
keys:
- data-cache-bst-auditory
- restore_cache:
keys:
- data-cache-bst-resting
- restore_cache:
keys:
- data-cache-fieldtrip
- restore_cache:
keys:
- data-cache-somato
- restore_cache:
keys:
- data-cache-hf-sef
- restore_cache:
keys:
- data-cache-opm
- restore_cache:
keys:
- data-cache-sample
- restore_cache:
keys:
- data-cache-spm-face
- restore_cache:
keys:
- data-cache-testing
- restore_cache:
keys:
- data-cache-visual
- restore_cache:
keys:
- data-cache-ucl-opm-auditory
- restore_cache:
keys:
- data-cache-phantom-kit
- restore_cache:
keys:
- data-cache-ds004388
- run:
name: Get data
# This limit could be increased, but this is helpful for finding slow ones
# (even ~2GB datasets should be downloadable in this time from good
# providers)
no_output_timeout: 10m
command: |
./tools/circleci_download.sh
- run:
name: Verify build type
command: |
echo "PATTERN=$(cat pattern.txt)"
echo "BUILD=$(cat build.txt)"
ls -al ~/mne_data;
# Run doctest (if it's full or front) before building the docs
- run:
name: make test-doc
command: |
if [[ $(cat gitlog.txt) == *"[circle front]"* ]] || [[ $(cat build.txt) == "html-memory" ]] ; then
make test-doc;
mkdir -p doc/_build/test-results/test-doc;
cp junit-results.xml doc/_build/test-results/test-doc/junit.xml;
fi;
# Build docs
- run:
name: make html
command: | # we have -o pipefail in #BASH_ENV so we should be okay
set -x
PATTERN=$(cat pattern.txt) make -C doc $(cat build.txt) 2>&1 | tee sphinx_log.txt
- run:
name: Check sphinx log for warnings (which are treated as errors)
when: always
command: |
! grep "^.*\(WARNING\|ERROR\): " sphinx_log.txt
- run:
name: Show profiling output
when: always
command: |
if compgen -G "doc/*.dat" > /dev/null; then
mkdir -p doc/generated
mprof plot doc/*.dat --output doc/generated/memory.png
else
echo "No profile data found in doc/"
fi
- run:
name: Sanity check system state
command: |
python -c "import mne; level = mne.get_config('MNE_LOGGING_LEVEL'); assert level.lower() == 'info', repr(level)"
# Reduce upload time of artifacts we will (almost) never look at
- run:
name: Reduce artifact upload time
command: |
if grep -q html-pattern-memory build.txt; then
zip -rm doc/_build/html/_downloads.zip doc/_build/html/_downloads
fi
for NAME in generated auto_tutorials auto_examples; do
zip -rm doc/${NAME}.zip doc/${NAME}
done
# Save the JUnit file
- store_test_results:
path: doc/_build/test-results
- store_artifacts:
path: doc/_build/test-results
destination: test-results
# Save the SG RST
- store_artifacts:
path: doc/auto_examples.zip
- store_artifacts:
path: doc/auto_tutorials.zip
- store_artifacts:
path: doc/generated.zip
# Save the HTML
- store_artifacts:
path: doc/_build/html/
destination: html
- persist_to_workspace:
root: doc/_build
paths:
- html
# Keep these separate, maybe better in terms of size limitations (?)
- save_cache:
key: data-cache-tiny-0 # < 100 M, might as well combine
paths:
- ~/.mne
- ~/mne_data/MNE-kiloword-data # (28 M)
- ~/mne_data/MNE-eegbci-data # (35 M)
- ~/mne_data/MNE-misc-data # (39 M)
- ~/mne_data/mTRF_1.5 # (56 M)
- ~/mne_data/MNE-phantom-4DBTi # (77 M)
- save_cache:
key: data-cache-tiny-1 # more to combine
paths:
- ~/mne_data/MNE-fNIRS-motor-data # (71 M)
- ~/mne_data/MNE-refmeg-noise-data # (93 M)
- ~/mne_data/physionet-sleep-data # (95 M)
- save_cache:
key: data-cache-multimodal
paths:
- ~/mne_data/MNE-multimodal-data # (240 M)
- save_cache:
key: data-cache-limo
paths:
- ~/mne_data/MNE-limo-data # (244 M)
- save_cache:
key: data-cache-fsaverage
paths:
- ~/mne_data/MNE-fsaverage-data # (762 M)
- save_cache:
key: data-cache-bst-raw
paths:
- ~/mne_data/MNE-brainstorm-data/bst_raw # (830 M)
- save_cache:
key: data-cache-bst-phantom-ctf
paths:
- ~/mne_data/MNE-brainstorm-data/bst_phantom_ctf # (177 M)
- save_cache:
key: data-cache-bst-phantom-elekta
paths:
- ~/mne_data/MNE-brainstorm-data/bst_phantom_elekta # (1.4 G)
- save_cache:
key: data-cache-bst-phantom-kernel
paths:
- ~/mne_data/MNE-phantom-kernel-data # (362 M)
- save_cache:
key: data-cache-bst-auditory
paths:
- ~/mne_data/MNE-brainstorm-data/bst_auditory # (2.9 G)
- save_cache:
key: data-cache-bst-resting
paths:
- ~/mne_data/MNE-brainstorm-data/bst_resting # (4.5 G)
- save_cache:
key: data-cache-fieldtrip
paths:
- ~/mne_data/MNE-fieldtrip_cmc-data # (699 M)
- save_cache:
key: data-cache-somato
paths:
- ~/mne_data/MNE-somato-data # (750 M)
- save_cache:
key: data-cache-hf-sef
paths:
- ~/mne_data/HF_SEF # (1.3 G)
- save_cache:
key: data-cache-opm
paths:
- ~/mne_data/MNE-OPM-data # (1.9 G)
- save_cache:
key: data-cache-sample
paths:
- ~/mne_data/MNE-sample-data # (3.2 G)
- save_cache:
key: data-cache-spm-face
paths:
- ~/mne_data/MNE-spm-face # (1.5 G)
- save_cache:
key: data-cache-testing
paths:
- ~/mne_data/MNE-testing-data # (2.5 G)
- save_cache:
key: data-cache-visual
paths:
- ~/mne_data/MNE-visual_92_categories-data # (6 G)
- save_cache:
key: data-cache-ucl-opm-auditory
paths:
- ~/mne_data/auditory_OPM_stationary # (4 G)
- save_cache:
key: data-cache-phantom-kit
paths:
- ~/mne_data/MNE-phantom-KIT-data # (1 G)
- save_cache:
key: data-cache-ds004388
paths:
- ~/mne_data/ds004388 # (1.8 G)
linkcheck:
# there are a few files excluded from this for expediency, see Makefile
parameters:
scheduled:
type: string
default: "false"
machine:
image: ubuntu-2404:current
resource_class: large
steps:
- restore_cache:
keys:
- source-cache
- checkout
- run:
name: Check-skip
command: |
export COMMIT_MESSAGE=$(git log --format=oneline -n 1);
if [[ "$COMMIT_MESSAGE" != *"[circle linkcheck]"* ]] && [ "<< parameters.scheduled >>" != "true" ]; then
echo "Skip detected, exiting job ${CIRCLE_JOB}."
circleci-agent step halt;
fi
- run:
name: Set BASH_ENV
command: ./tools/circleci_bash_env.sh
- restore_cache:
keys:
- pip-cache-0
- run:
name: Get Python running
command: |
./tools/circleci_dependencies.sh
- run:
name: Check installation
command: |
mne sys_info -pd
- run:
name: make linkcheck
no_output_timeout: 40m
command: |
make -C doc linkcheck
- store_artifacts:
path: doc/_build/linkcheck
destination: linkcheck
deploy:
machine:
image: ubuntu-2404:current
steps:
- attach_workspace:
at: /tmp/build
- restore_cache:
keys:
- website-cache
- run:
name: Set BASH_ENV
command: |
set -e
echo "set -e" >> $BASH_ENV
# Don't try to deploy if nothing is there or not on the right branch
- run:
name: Check docs
command: |
if [ ! -f /tmp/build/html/index.html ] ; then
echo "No files found to upload (build: ${CIRCLE_BRANCH}).";
circleci-agent step halt;
fi;
- run:
name: Fetch docs
command: |
mkdir -p ~/.ssh
echo -e "Host *\nStrictHostKeyChecking no" > ~/.ssh/config
chmod og= ~/.ssh/config
if [ ! -d ~/mne-tools.github.io ]; then
git clone git@github.com:/mne-tools/mne-tools.github.io.git ~/mne-tools.github.io --depth=1
fi
- run:
name: Deploy docs
command: |
git config --global user.email "circle@mne.tools";
git config --global user.name "Circle CI";
cd ~/mne-tools.github.io;
git checkout main
git remote -v
git fetch origin
git reset --hard origin/main
git clean -xdf
if [ "${CIRCLE_BRANCH}" == "main" ]; then
echo "Deploying dev docs for ${CIRCLE_BRANCH}.";
rm -Rf dev;
cp -a /tmp/build/html dev;
git add -A;
git commit -m "CircleCI update of dev docs (${CIRCLE_BUILD_NUM}).";
else
echo "Deploying stable docs for ${CIRCLE_BRANCH}.";
rm -Rf stable;
cp -a /tmp/build/html stable;
git add -A;
git commit -m "CircleCI update of stable docs (${CIRCLE_BUILD_NUM}).";
fi;
git push origin main;
- save_cache:
key: website-cache
paths:
- ~/mne_data/MNE-visual_92_categories-data
workflows:
default:
jobs:
- build_docs:
name: build_docs
- linkcheck:
name: linkcheck
- deploy:
name: deploy
requires:
- build_docs
filters:
branches:
only:
- main
- /maint\/.*/
main:
jobs:
- build_docs:
scheduled: "true"
name: build_docs_main
- deploy:
name: deploy_main
requires:
- build_docs_main
triggers:
- schedule:
# "At 6:00 AM GMT every day"
cron: "0 6 * * *"
filters:
branches:
only:
- main
monthly:
jobs:
- linkcheck:
name: linkcheck_monthly
scheduled: "true"
triggers:
- schedule:
# "At 6:00 AM GMT on the first day of each month"
cron: "0 6 1 * *"
filters:
branches:
only:
- main