From 78fea268b87be7f5ac90c108fb4f7a7b524e31da Mon Sep 17 00:00:00 2001 From: Maciej Olko Date: Tue, 15 Jul 2025 14:26:24 +0200 Subject: [PATCH 1/2] gh-136155: Docs: check for EPUB fatal errors in CI (GH-134074) (cherry picked from commit 624bf52c83abcb1f948f9059e29729fa94d38086) Co-authored-by: Maciej Olko Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> --- .github/workflows/reusable-docs.yml | 14 ++++++++++- Doc/conf.py | 1 + Doc/tools/check-epub.py | 24 +++++++++++++++++++ ...-07-01-23-00-58.gh-issue-136155.4siQQO.rst | 1 + 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 Doc/tools/check-epub.py create mode 100644 Misc/NEWS.d/next/Documentation/2025-07-01-23-00-58.gh-issue-136155.4siQQO.rst diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index 657e0a6bf662f7..7b9dc4818577eb 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -66,7 +66,7 @@ jobs: run: | set -Eeuo pipefail # Build docs with the nit-picky option; write warnings to file - make -C Doc/ PYTHON=../python SPHINXOPTS="--quiet --nitpicky --fail-on-warning --warning-file sphinx-warnings.txt" html + make -C Doc/ PYTHON=../python SPHINXOPTS="--quiet --nitpicky --warning-file sphinx-warnings.txt" html - name: 'Check warnings' if: github.event_name == 'pull_request' run: | @@ -75,6 +75,18 @@ jobs: --fail-if-regression \ --fail-if-improved \ --fail-if-new-news-nit + - name: 'Build EPUB documentation' + continue-on-error: true + run: | + set -Eeuo pipefail + make -C Doc/ PYTHON=../python SPHINXOPTS="--quiet" epub + pip install epubcheck + epubcheck Doc/build/epub/Python.epub &> Doc/epubcheck.txt + - name: 'Check for fatal errors in EPUB' + if: github.event_name == 'pull_request' + continue-on-error: true # until gh-136155 is fixed + run: | + python Doc/tools/check-epub.py # Run "doctest" on HEAD as new syntax doesn't exist in the latest stable release doctest: diff --git a/Doc/conf.py b/Doc/conf.py index 85c7f495e86146..35e0b3eaeafe94 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -448,6 +448,7 @@ epub_author = 'Python Documentation Authors' epub_publisher = 'Python Software Foundation' +epub_exclude_files = ('index.xhtml', 'download.xhtml') # index pages are not valid xhtml # https://github.com/sphinx-doc/sphinx/issues/12359 diff --git a/Doc/tools/check-epub.py b/Doc/tools/check-epub.py new file mode 100644 index 00000000000000..693dc239c8ad58 --- /dev/null +++ b/Doc/tools/check-epub.py @@ -0,0 +1,24 @@ +import sys +from pathlib import Path + + +def main() -> int: + wrong_directory_msg = "Must run this script from the repo root" + if not Path("Doc").exists() or not Path("Doc").is_dir(): + raise RuntimeError(wrong_directory_msg) + + with Path("Doc/epubcheck.txt").open(encoding="UTF-8") as f: + messages = [message.split(" - ") for message in f.read().splitlines()] + + fatal_errors = [message for message in messages if message[0] == "FATAL"] + + if fatal_errors: + print("\nError: must not contain fatal errors:\n") + for error in fatal_errors: + print(" - ".join(error)) + + return len(fatal_errors) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Misc/NEWS.d/next/Documentation/2025-07-01-23-00-58.gh-issue-136155.4siQQO.rst b/Misc/NEWS.d/next/Documentation/2025-07-01-23-00-58.gh-issue-136155.4siQQO.rst new file mode 100644 index 00000000000000..70f54936c80f55 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2025-07-01-23-00-58.gh-issue-136155.4siQQO.rst @@ -0,0 +1 @@ +We are now checking for fatal errors in EPUB builds in CI. From acad3f29a6b44e482600cfa830df254e4a01f3b5 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Fri, 8 Aug 2025 03:32:20 +0200 Subject: [PATCH 2/2] GH-136155: Fail the EPUB check on fatal errors (#137351) --- .github/workflows/reusable-docs.yml | 39 ++++++++++++++++++++--------- Doc/tools/check-epub.py | 36 +++++++++++++++----------- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index 7b9dc4818577eb..65154aae4c41d5 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -75,18 +75,6 @@ jobs: --fail-if-regression \ --fail-if-improved \ --fail-if-new-news-nit - - name: 'Build EPUB documentation' - continue-on-error: true - run: | - set -Eeuo pipefail - make -C Doc/ PYTHON=../python SPHINXOPTS="--quiet" epub - pip install epubcheck - epubcheck Doc/build/epub/Python.epub &> Doc/epubcheck.txt - - name: 'Check for fatal errors in EPUB' - if: github.event_name == 'pull_request' - continue-on-error: true # until gh-136155 is fixed - run: | - python Doc/tools/check-epub.py # Run "doctest" on HEAD as new syntax doesn't exist in the latest stable release doctest: @@ -114,3 +102,30 @@ jobs: # Use "xvfb-run" since some doctest tests open GUI windows - name: 'Run documentation doctest' run: xvfb-run make -C Doc/ PYTHON=../python SPHINXERRORHANDLING="--fail-on-warning" doctest + + check-epub: + name: 'Check EPUB' + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: 'Set up Python' + uses: actions/setup-python@v5 + with: + python-version: '3' + cache: 'pip' + cache-dependency-path: 'Doc/requirements.txt' + - name: 'Install build dependencies' + run: | + make -C Doc/ venv + python -m pip install epubcheck + - name: 'Build EPUB documentation' + run: make -C Doc/ PYTHON=../python epub + - name: 'Run epubcheck' + continue-on-error: true + run: epubcheck Doc/build/epub/Python.epub &> Doc/epubcheck.txt + - run: cat Doc/epubcheck.txt + - name: 'Check for fatal errors in EPUB' + run: python Doc/tools/check-epub.py diff --git a/Doc/tools/check-epub.py b/Doc/tools/check-epub.py index 693dc239c8ad58..6a10096c117542 100644 --- a/Doc/tools/check-epub.py +++ b/Doc/tools/check-epub.py @@ -1,24 +1,30 @@ -import sys from pathlib import Path +CPYTHON_ROOT = Path( + __file__, # cpython/Doc/tools/check-epub.py + '..', # cpython/Doc/tools + '..', # cpython/Doc + '..', # cpython +).resolve() +EPUBCHECK_PATH = CPYTHON_ROOT / 'Doc' / 'epubcheck.txt' -def main() -> int: - wrong_directory_msg = "Must run this script from the repo root" - if not Path("Doc").exists() or not Path("Doc").is_dir(): - raise RuntimeError(wrong_directory_msg) - - with Path("Doc/epubcheck.txt").open(encoding="UTF-8") as f: - messages = [message.split(" - ") for message in f.read().splitlines()] - fatal_errors = [message for message in messages if message[0] == "FATAL"] +def main() -> int: + lines = EPUBCHECK_PATH.read_text(encoding='utf-8').splitlines() + fatal_errors = [line for line in lines if line.startswith('FATAL')] if fatal_errors: - print("\nError: must not contain fatal errors:\n") - for error in fatal_errors: - print(" - ".join(error)) + err_count = len(fatal_errors) + s = 's' * (err_count != 1) + print() + print(f'Error: epubcheck reported {err_count} fatal error{s}:') + print() + print('\n'.join(fatal_errors)) + return 1 - return len(fatal_errors) + print('Success: no fatal errors found.') + return 0 -if __name__ == "__main__": - sys.exit(main()) +if __name__ == '__main__': + raise SystemExit(main())