Skip to content

Commit b970ed7

Browse files
committed
check: add notebook format check script (issue #1274)
Add check/nbformat, a new bash script that checks the format of all tracked Jupyter notebook (.ipynb) files using the TensorFlow Docs notebook format checker (tensorflow_docs.tools.nbformat). The script: - Discovers all .ipynb files tracked by git via git ls-files - Runs python -m tensorflow_docs.tools.nbformat on each notebook - Defaults to check-only mode (non-destructive) - Accepts --fix to reformat notebooks in place - Accepts --help to print usage information - Exits non-zero if any notebook fails the format check Wire check/nbformat into: - check/all: called unconditionally alongside check/mypy and check/shellcheck, since notebook format is structural and not related to which Python files changed - check/shellcheck: added to the required_shell_scripts list (in sorted order) so shellcheck verifies the new script is present Add tensorflow-docs to dev_tools/requirements/deps/pytest.txt alongside the existing nbformat dep, since both are needed for notebook-related checks and belong in the same env grouping. Add a notebook-checks CI job to .github/workflows/ci.yaml that triggers when .ipynb files change and runs check/nbformat using the pytest.env.txt environment (which includes tensorflow-docs). Also add the new job to report-results so it is a required check.
1 parent a51cdcc commit b970ed7

5 files changed

Lines changed: 146 additions & 0 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ jobs:
102102

103103
shell: ${{steps.filter.outputs.shell || steps.filter.outputs.ci}}
104104
shell_files: ${{steps.filter.outputs.shell_files}}
105+
106+
notebooks: ${{steps.filter.outputs.notebooks || steps.filter.outputs.ci}}
105107
steps:
106108
- name: Check out a copy of the OpenFermion git repository
107109
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -135,6 +137,8 @@ jobs:
135137
shell:
136138
- '**/*.sh'
137139
- 'check/*'
140+
notebooks:
141+
- '**/*.ipynb'
138142
139143
python-checks:
140144
if: needs.changes.outputs.python == 'true'
@@ -418,13 +422,40 @@ jobs:
418422
# shellcheck disable=SC2086
419423
shellcheck ${CHANGED_FILES}
420424
425+
notebook-checks:
426+
if: needs.changes.outputs.notebooks == 'true'
427+
name: Notebook format checks
428+
needs: changes
429+
runs-on: ubuntu-24.04
430+
timeout-minutes: 10
431+
steps:
432+
- name: Check out a copy of the git repository
433+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
434+
435+
- name: Set up Python and restore cache
436+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
437+
with:
438+
python-version: ${{inputs.python_ver || env.python_ver}}
439+
cache: pip
440+
cache-dependency-path: ${{env.python_dep_files}}
441+
442+
- name: Upgrade pip
443+
run: python -m pip install --upgrade pip
444+
445+
- name: Install requirements
446+
run: pip install -r dev_tools/requirements/envs/pytest.env.txt
447+
448+
- name: Check notebook format
449+
run: check/nbformat
450+
421451
report-results:
422452
name: CI
423453
if: always()
424454
needs:
425455
- coverage
426456
- docker-lint
427457
- json-lint
458+
- notebook-checks
428459
- pytest
429460
- pytest-compat
430461
- pytest-extra

check/all

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ fi
9999
run check/mypy
100100
run check/pytest-and-incremental-coverage "${rev[@]}"
101101
run check/shellcheck
102+
run check/nbformat
102103

103104
# ~~~~ Summarize the results and exit with a status code ~~~~
104105

check/nbformat

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env bash
2+
# Copyright 2025 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
################################################################################
17+
# Checks the format of Jupyter notebook (.ipynb) files using the
18+
# TensorFlow Docs notebook format checker.
19+
#
20+
# Usage:
21+
# check/nbformat [--help] [--fix]
22+
#
23+
# By default, this script checks all .ipynb files tracked by git and exits
24+
# with a non-zero code if any notebook does not conform to the expected format.
25+
#
26+
# With '--fix', the script reformats the notebooks in place instead of just
27+
# reporting errors.
28+
################################################################################
29+
30+
set -euo pipefail
31+
32+
declare -r usage="\
33+
Usage: ${0##*/} [--help] [--fix]
34+
35+
Checks the format of Jupyter notebook (.ipynb) files in this repository using
36+
the TensorFlow Docs notebook format checker (tensorflow_docs.tools.nbformat).
37+
38+
With no arguments, notebooks are checked but not modified. Exits with a
39+
non-zero status code if any notebook fails the format check.
40+
41+
With --fix, the notebooks are reformatted in place and the script exits with
42+
status 0 if all notebooks were successfully processed.
43+
"
44+
45+
# Get the working directory to the repo root.
46+
thisdir=$(dirname "${BASH_SOURCE[0]:?}")
47+
repo_dir=$(git -C "${thisdir}" rev-parse --show-toplevel)
48+
cd "${repo_dir}"
49+
50+
# Parse arguments.
51+
opt_fix=0
52+
for arg in "$@"; do
53+
case "${arg}" in
54+
-h | --help)
55+
echo "${usage}"
56+
exit 0
57+
;;
58+
--fix)
59+
opt_fix=1
60+
;;
61+
*)
62+
echo "Unknown argument: '${arg}'" >&2
63+
echo "See '${0} --help' for usage." >&2
64+
exit 1
65+
;;
66+
esac
67+
done
68+
69+
# Find all tracked .ipynb files in the repository.
70+
IFS=$'\n' read -r -d '' -a notebooks < <(
71+
git ls-files '*.ipynb'
72+
printf "\0"
73+
)
74+
75+
if [[ "${#notebooks[@]}" -eq 0 ]]; then
76+
echo "No .ipynb notebook files found."
77+
exit 0
78+
fi
79+
80+
echo "Found ${#notebooks[@]} notebook(s)."
81+
82+
# Build the tensorflow_docs.tools.nbformat argument list.
83+
declare -a tf_args=()
84+
if (( opt_fix )); then
85+
echo "Running notebook format fixer..."
86+
tf_args=( "--fix" )
87+
else
88+
echo "Running notebook format checker..."
89+
fi
90+
91+
declare -a errors=()
92+
for notebook in "${notebooks[@]}"; do
93+
echo " Checking: ${notebook}"
94+
if ! python -m tensorflow_docs.tools.nbformat "${tf_args[@]}" "${notebook}"; then
95+
errors+=( "${notebook}" )
96+
fi
97+
done
98+
99+
echo
100+
101+
if [[ "${#errors[@]}" -eq 0 ]]; then
102+
echo "All notebooks passed the format check."
103+
exit 0
104+
else
105+
echo "The following notebooks failed the format check:" >&2
106+
printf " %s\n" "${errors[@]}" >&2
107+
if (( ! opt_fix )); then
108+
echo "" >&2
109+
echo "Run 'check/nbformat --fix' to fix the notebooks automatically." >&2
110+
fi
111+
exit 1
112+
fi

check/shellcheck

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ required_shell_scripts=(
5757
check/all
5858
check/format-incremental
5959
check/mypy
60+
check/nbformat
6061
check/pylint
6162
check/pylint-changed-files
6263
check/pytest

dev_tools/requirements/deps/pytest.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ pytest-retry
66
pytest-xdist
77

88
nbformat
9+
tensorflow-docs

0 commit comments

Comments
 (0)