Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CI #5

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
5 changes: 5 additions & 0 deletions .dictionary.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
hist
livetime
iff
amin
amax
31 changes: 31 additions & 0 deletions .github/workflows/execution_order.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Check execution order of notebooks

on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:

jobs:
check_versions:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9]

steps:
- uses: actions/checkout@v2
- name: Check execution order of notebooks
run: |
for file in *.ipynb; do
echo "Checking ${file}";
if ! ./tests/check_execution_order.py "${file}"; then
echo "::error file=${file},title=Error";
exit 1;
fi
done

39 changes: 39 additions & 0 deletions .github/workflows/run.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Check that the code runs

on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:

jobs:
run:
name: Run the notebooks

runs-on: ubuntu-latest
defaults:
run:
shell: bash -l {0}
strategy:
matrix:
python-version: [3.9]

steps:
- uses: actions/checkout@v2
- uses: conda-incubator/setup-miniconda@v3
- name: Installing requirements
run: |
conda env create -f environment.yml
- name: Test notebooks execution
run: |
conda activate introduction_gwosc_data
for file in *.ipynb; do
echo "Checking ${file}";
if ! ./tests/check_run.sh "${file}"; then
echo "::error file=${file},title=Error";
exit 1;
fi
done
42 changes: 42 additions & 0 deletions .github/workflows/spelling.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Check spelling

on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:

jobs:
spellcheck:

runs-on: ubuntu-latest
defaults:
run:
shell: bash -l {0}
strategy:
matrix:
python-version: [3.9]

steps:
- uses: actions/checkout@v2
- uses: conda-incubator/setup-miniconda@v3
- name: Installing requirements
run: |
conda env create -f environment.yml
- name: Spellchecking notebooks
run: |
conda activate introduction_gwosc_data
for file in *.ipynb; do
echo "Checking ${file}";
if ! ./tests/check_spelling.sh "${file}"; then
echo "::error file=${file},title=Error";
exit 1;
fi
done
- name: Spellchecking other files
run: |
conda activate introduction_gwosc_data
codespell -I .dictionary.txt --skip="*.ipynb" .
31 changes: 31 additions & 0 deletions .github/workflows/versions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Check version

on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:

jobs:
check_versions:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9]

steps:
- uses: actions/checkout@v2
- name: Check versions in notebooks
run: |
for file in *.ipynb; do
echo "Checking ${file}";
if ! ./tests/check_versions.py "${file}"; then
echo "::error file=${file},title=Error";
exit 1;
fi
done

4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# We ignore this directory (and its content)
# It is used by our CI scripts to store their content
/tmp/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
19 changes: 5 additions & 14 deletions 01 - Download GWOSC Data.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -217,16 +217,7 @@
"execution_count": 1,
"id": "05f420e4-c63e-4156-a5ba-ea911ff0dbf8",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/mdubois/Projects/LVK/GWOSC/introduction_gwosc_data_files/venv/lib/python3.9/site-packages/urllib3/__init__.py:35: NotOpenSSLWarning: urllib3 v2 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'LibreSSL 2.8.3'. See: https://github.com/urllib3/urllib3/issues/3020\n",
" warnings.warn(\n"
]
}
],
"outputs": [],
"source": [
"import requests\n",
"\n",
Expand Down Expand Up @@ -319,10 +310,10 @@
" return filename\n",
"\n",
"\n",
"for afile in strain_files:\n",
" if afile[\"GPSstart\"] == 1264312320 and afile[\"format\"] == \"hdf5\":\n",
" print(f\"Downloading {afile['url']}\")\n",
" fname = download_strain_file(afile[\"url\"])"
"for a_file in strain_files:\n",
" if a_file[\"GPSstart\"] == 1264312320 and a_file[\"format\"] == \"hdf5\":\n",
" print(f\"Downloading {a_file['url']}\")\n",
" fname = download_strain_file(a_file[\"url\"])"
]
},
{
Expand Down
86 changes: 86 additions & 0 deletions Contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ If it's not the case, read [this introduction](https://jupyter.org/try-jupyter/n

## Contribution workflow

### Basic workflow

The workflow to contribute is based on GitHub.

The first thing to do if you want to improve the tutorials is to open an issue on GitHub to describe your idea.
Expand All @@ -20,6 +22,17 @@ This issue will be used to discuss the best way to tackle the problem.
If you want to work on this issue, you will have to fork the repository in your GitHub account,
create a branch and open a pull request once the modification is done.

### CI rules

The GitHub repository contains CI rules to ensure that some quality criteria are met before merging a PR.

Here is a short description of the rules:

- ensure that the code in the notebook runs
- ensure that cells are run in order
- ensure that notebooks don't install packages that are not in the conda environment
- ensure that there is no spelling mistakes in the files

## Notebook formatting

The format of Jupyter notebooks is hard to version properly.
Expand Down Expand Up @@ -92,6 +105,8 @@ Such admonition should go in a separate cell.
Before committing, it's good to re-run the notebook from scratch
as it allows to ensure that there is no bug
and to have nicely ordered cells.
As explained above the CI rules enforce this so get used to this,
it will help when opening the PR.

To do so, open the "Run" menu and click "Restart Kernel and Run All Cells".

Expand All @@ -112,3 +127,74 @@ This specific code should be placed in a dedicated cell near the top of the note
and commented out by default.
Also, add a warning before such cells.
Have look at [tutorial 5](<./05 - GWpy Examples.ipynb>) for an example.

### `git` pre-commit hook

The CI rules described above are based on scripts that stored in this repository.
For advanced users, we recommend you use those scripts as `git` pre-commit hook
so you will minimize surprises during the PR.
Find below an example:

```bash
#!/usr/bin/env bash
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# The script is run at the root of the repository

#set -x

set -e
set -u
set -o pipefail

# This is used to determine against what to compare:
# if the repository is not empty, it's HEAD
# otherwise against the hash of an empty directory
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=$(git hash-object -t tree /dev/null)
fi

# Redirect output to stderr.
exec 1>&2

# Get the list of modified files
# We want all modified files except the delete ones (hence --diff-filter=d)
declare -a modified_files
OLD_IFS="${IFS}"
IFS=$'\n'
modified_files=( $(git diff --cached --name-only -z --diff-filter=d $against | tr '\0' '\n') )
IFS="${OLD_IFS}"

if [[ "${#modified_files}" -eq 0 ]]
then
echo "Nothing to check"
exit 0
fi

echo "Gonna check ${#modified_files[@]} file(s): ${modified_files[@]}"

echo "Checking modified files"
for modified_file in "${modified_files[@]}"
do
if [[ "${modified_file}" =~ \.ipynb$ ]]
then
echo "Checking notebook ${modified_file}"
./tests/check_run.sh "${modified_file}"
./tests/check_spelling.sh "${modified_file}"
./tests/check_versions.py "${modified_file}"
./tests/check_execution_order.py "${modified_file}"
else
echo "Checking non-notebook ${modified_file}"
# Spell check other files
codespell -I .dictionary.txt --skip="*.ipynb" "${modified_file}"
fi
done
```
2 changes: 2 additions & 0 deletions Contributors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The CI actions are based on work by Gregory Ashton
for ODW 2023 (https://github.com/gw-odw/odw-2023/).
1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ dependencies:
- matplotlib=3.9.2
- scipy=1.14.1
- gwpy=3.0.10
- codespell=2.3.0
48 changes: 48 additions & 0 deletions tests/check_execution_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env python

# Check that the cells are executed in order
# To do so we open the notebook and inspects the cells

import argparse
import json
import sys
import re
import yaml


# CLI
parser = argparse.ArgumentParser(
description='Check that the cells are executed in order',
)
parser.add_argument('notebook')

# Parse CLI
args = parser.parse_args()
fname = args.notebook

# Check cell order
errors = []
with open(fname, "r") as file:
content = json.load(file)
last_execution_count = 0
for cell in content["cells"]:
try:
execution_count = cell["execution_count"]
except KeyError:
#print("Markdown cell", file=sys.stderr)
continue
#print(f"Expected: {last_execution_count+1}, Got: {execution_count}")
if execution_count != last_execution_count + 1:
msg = f"Cell {execution_count} should be {last_execution_count+1}"
errors.append(msg)
last_execution_count += 1

if len(errors) > 0:
errors = "\n".join(errors)
msg = f"Execution order issues in {fname}:\n"
msg += f"{errors}"
print(msg)
sys.exit(1)
else:
print(f"No execution order issue in {fname}")
sys.exit(0)
Loading
Loading