Skip to content

add doc:links and docs:links:check #427

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

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ jobs:
run: |
poetry run -- nox -s docs:build

Documentation-Links:
name: Doc Links Check
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: SCM Checkout
uses: actions/checkout@v4

- name: Setup Python & Poetry Environment
uses: ./.github/actions/python-environment

- name: Link Check
Copy link
Collaborator

@ArBridgeman ArBridgeman Jun 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. As it's related to the documents, would recommend moving poetry run -- nox -s docs:links:check to run inside of the Documentation jobs, either just before or after poetry run -- nox -s docs:build is executed.

Reasons: There are quite a few repos that don't have documentation. While they can remove it, it's slightly easier if it's in one github job. Additionally, if we kept it as it is in this PR, we should add it to the need list for the Tests job. Additionally, it's taking around 16s to run, so it doesn't extremely benefit from being run in parallel.

  1. Could you additionally modify the need list for the Tests to include the Changelog job? Please also modify the template workflow file for checks.yml with the same changes (+ docs:links:check & Test>need(changelog)).

run: |
poetry run -- nox -s docs:links:check

Changelog:
name: Changelog Update Check
runs-on: ubuntu-24.04
Expand Down
1 change: 1 addition & 0 deletions doc/changes/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ At this time, PTB currently does not support setting up SonarQube for a **privat

## ✨ Features
Copy link
Collaborator

@ArBridgeman ArBridgeman Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. At the top of the docs/changes/unreleased.md, please add to the ## Summary section. Below is some suggested text.
  2. Please modify the suggested to explain why you selected certain values & what they do.
  3. Please, above the Sonar part, add ### Sonar.

Links in Documentation

This version of the PTB adds nox tasks to check links present in our documentation:

  • docs:link - List all the links within the documentation
  • docs:links:check - Checks whether all links in the documentation are accessible

docs:links:check is run in the CI checks.yml. If this step fails in the CI, please check the output & manually resolve the issues. There might be some cases where you need to update your doc/conf.py with specific values for the allowed options for the Linkcheck Builder.

We recommend the following values be added

linkcheck_rate_limit_timeout = 40
linkcheck_timeout = 5
linkcheck_delay = 20
linkcheck_retries = 2
linkcheck_anchors = False
linkcheck_ignore: list[str] = []
linkcheck_allowed_redirects = {
    # All HTTP redirections from the source URI to
    # the canonical URI will be treated as "working".
    r"https://github\.com/.*": r"https://github\.com/login*"
}

* #451: Added nox task to execute pysonar & added Sonar to the CI
* #409: Doc link & checks

## ⚒️ Refactorings
* #451: Reduced scope of nox tasks `lint:code` (pylint) and `lint:security` (bandit) to analyze only the package code
12 changes: 12 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,15 @@
"github_url": "https://github.com/exasol/python-toolbox",
"accent_color": "grass",
}
# -- Configure link checking behavior ----------------------------------------
linkcheck_rate_limit_timeout = 40
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update project-template/{{cookiecutter.repo_name}}/doc/conf.py with values that you think are worthwhile to have.

linkcheck_timeout = 5
linkcheck_delay = 20
linkcheck_retries = 2
linkcheck_anchors = False
linkcheck_ignore: list[str] = []
linkcheck_allowed_redirects = {
# All HTTP redirections from the source URI to
# the canonical URI will be treated as "working".
r"https://github\.com/.*": r"https://github\.com/login*"
}
2 changes: 1 addition & 1 deletion doc/developer_guide/modules/sphinx/sphinx.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ sphinx
sphinx-multiversion
+++++++++++++++++++

The `sphinx-multiversion` extension is a modified copy of `Holzhaus/sphinx-multiversion <https://github.com/Holzhaus/sphinx-multiversion>`_. This copy was taken from version :code:`0.24.0`.
The `sphinx-multiversion` extension is a modified copy of `Holzhaus/sphinx-multiversion <https://github.com/sphinx-contrib/multiversion>`_. This copy was taken from version :code:`0.24.0`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The repository has moved; thus, the reference text should be updated to reflect that.

Suggested change
The `sphinx-multiversion` extension is a modified copy of `Holzhaus/sphinx-multiversion <https://github.com/sphinx-contrib/multiversion>`_. This copy was taken from version :code:`0.24.0`.
The `sphinx-multiversion` extension is a modified copy of `sphinx-contrib/multiversion <https://github.com/sphinx-contrib/multiversion>`_. This copy was taken from version :code:`0.24.0`.


It has been adjusted with minor code changes and modified defaults to work seamlessly with Exasol integration projects, which often require a specific project structure and layout. Additionally, it is designed to be used with an HTML theme that supports displaying and selecting multiple versions if the `versions` variable is set in the HTML context of sphinx. As of this writing, the theme used in conjunction with this modified version of `sphinx-multiversion` is `SHIBUYA <https://github.com/lepture/shibuya>`_, version :code:`2024.10.15`.

Expand Down
2 changes: 1 addition & 1 deletion doc/github_actions/security_issues.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,4 @@ Ideas
.. todo::

Consider adapting common CVE report format as input, for additional details
`see here <https://github.com/CVEProject/cve-schema/blob/master/schema/v5.0/CVE_JSON_5.0_schema.json>`_.
`see here <https://github.com/CVEProject/cve-schema/blob/main/schema/CVE_Record_Format.json>`_.
2 changes: 1 addition & 1 deletion doc/styleguide/guides/idioms/idioms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ where we picked it up from, rather than the "original" source.


.. _Raymond Hettinger: https://github.com/rhettinger
.. _Transform Code into Beautiful, Idiomatic Python: https://www.youtube.com/watch?v=OSGv2VnC0go>
.. _Transform Code into Beautiful, Idiomatic Python: https://www.youtube.com/watch?v=OSGv2VnC0go
.. _Transform Python Slides: https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1
4 changes: 2 additions & 2 deletions doc/styleguide/guides/style.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ _____
.. _Google Styleguide: https://google.github.io/styleguide/pyguide.html
.. _PEP 8: https://peps.python.org/pep-0008/
.. _Python Idioms: https://gist.github.com/0x4D31/f0b633548d8e0cfb66ee3bea6a0deff9
.. _Python Like You Mean It: http://www.pythonlikeyoumeanit.com/module_2.html>
.. _Python Like You Mean It: http://www.pythonlikeyoumeanit.com/module_2.html
.. _Python Programming Idioms: https://en.wikibooks.org/wiki/Python_Programming/Idioms

.. _Transform Code into Beautiful, Idiomatic Python: https://www.youtube.com/watch?v=OSGv2VnC0go>
.. _Transform Code into Beautiful, Idiomatic Python: https://www.youtube.com/watch?v=OSGv2VnC0go
.. _Transform Python Slides: https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1
.. _Stop Writing Classes: https://www.youtube.com/watch?v=o9pEzgHorH0
.. _Refactoring Python: https://www.youtube.com/watch?v=D_6ybDcU5gc
Expand Down
4 changes: 2 additions & 2 deletions doc/user_guide/features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ Uniform Project Layout
PTB expects a default project layout following "convention over configuration" when possible and reasonable.
See the cookie-cutter project template for details, which is part of the python-toolbox workspace and can be found in directory `project-template`.
You can also generate a project from the template to explore the default structure.
For more details on this, please check out section `"getting started" <getting_started.html>`_ section.
For more details on this, please check out section :ref:`Getting Started` section.

Nox
---

The most central tool when interacting with the toolbox is :code:`nox`, which is the task runner used across all of Exasol's Python-based projects.
The toolbox itself provides various standard tasks and a plugin mechanism to extend these tasks if needed. For more information regarding nox, please visit the `nox homepage <http://nox.thea.codes/en/stable/>`_.
The toolbox itself provides various standard tasks and a plugin mechanism to extend these tasks if needed. For more information regarding nox, please visit the `nox homepage <https://nox.thea.codes/en/stable/>`_.

Central files in regards to nox and the toolbox are:

Expand Down
2 changes: 2 additions & 0 deletions doc/user_guide/getting_started.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _Getting Started:

Getting Started
===============

Expand Down
96 changes: 92 additions & 4 deletions exasol/toolbox/nox/_documentation.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
from __future__ import annotations

import argparse
import json
import os
import re
import shutil
import subprocess
import sys
import tempfile
import webbrowser
from collections.abc import (
Container,
Iterable,
)
from itertools import repeat
from pathlib import Path
from typing import (
Optional,
Tuple,
)

import nox
import requests # type: ignore
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused import

Suggested change
import requests # type: ignore

from nox import Session

from exasol.toolbox.nox._shared import DOCS_OUTPUT_DIR
Expand All @@ -17,8 +33,6 @@

def _build_docs(session: nox.Session, config: Config) -> None:
session.run(
"poetry",
"run",
"sphinx-build",
"-W",
"-b",
Expand All @@ -30,8 +44,6 @@ def _build_docs(session: nox.Session, config: Config) -> None:

def _build_multiversion_docs(session: nox.Session, config: Config) -> None:
session.run(
"poetry",
"run",
"sphinx-multiversion",
f"{config.doc}",
DOCS_OUTPUT_DIR,
Expand Down Expand Up @@ -88,6 +100,82 @@ def clean_docs(_session: Session) -> None:
shutil.rmtree(docs_folder)


@nox.session(name="docs:links", python=False)
def docs_list_links(session: Session) -> None:
Copy link
Collaborator

@ArBridgeman ArBridgeman Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to create an integration test and/or unit test(s) similar to https://github.com/exasol/python-toolbox/blob/main/test/unit/util/dependencies/poetry_dependencies_test.py.

For the integration test:

  • Within a tmp directory, make a simple set up with 1 to 3 links & test that what you capture on the capsys matches what you would expect.

For unit test(s):

It is true that we should not test everything that sphinx-build linkcheck does, but as we are building a small part of code (line 124-136) around it, it is best to ensure that the given certain input that we get an expected output. In the example test code (poetry_dependencies_test.py.) above, this was necessitated as when we moved from poetry 1.x to poetry 2.x the format of the pyproject.toml changed, but we had only hard-coded tests that represented a format that no longer existed. An analogous situation could be when we updated sphinx-build that we find the the JSONL format has changed or that the conf.py variable names have changed.

Copy link
Collaborator

@ArBridgeman ArBridgeman Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(In the example with poetry, there was no CI indication before that the code was failing. I just happened to see that the output in the GitHub summary wasn't correct. 😬 . Better to do light testing & catch it earlier on. Tests are relatively free. Developers finding issues isn't as much; we cost more per hour 😉 )

"""List all the links within the documentation."""
with tempfile.TemporaryDirectory() as path:
tmpdir = Path(path)
sp = subprocess.run(
[
"sphinx-build",
"-b",
"linkcheck",
"-D",
"linkcheck_ignore=.*",
PROJECT_CONFIG.root / "doc",
tmpdir,
],
)
print(sp.returncode)
if sp.returncode >= 2:
print(sp.stderr)
session.error(2)
output = tmpdir / "output.json"
links = output.read_text().split("\n")
file_links = []
for link in links:
if link != "":
line = json.loads(link)
if not line["uri"].startswith("#"):
file_links.append(line)
file_links.sort(key=lambda file: file["filename"])
print(
"\n".join(
f"filename: {fl['filename']} -> uri: {fl['uri']}" for fl in file_links
)
)


@nox.session(name="docs:links:check", python=False)
def docs_links_check(session: Session) -> None:
"""Checks whether all links in the documentation are accessible."""
parser = argparse.ArgumentParser(
prog="nox -s release:prepare",
usage="nox -s release:prepare -- [-h] [-o |--output]",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"-o", "--output", type=Path, help="path to output file", default=None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"-o", "--output", type=Path, help="path to output file", default=None
"-o", "--output", type=Path, help="path to copy the output json file", default=None

)
args = parser.parse_args(session.posargs)
with tempfile.TemporaryDirectory() as path:
tmpdir = Path(path)
sp = subprocess.run(
[
"sphinx-build",
"-b",
"linkcheck",
PROJECT_CONFIG.root / "doc",
tmpdir,
],
)
print(sp.returncode)
if sp.returncode >= 2:
print(sp.stderr)
session.error(2)
if args.output:
result_json = tmpdir / "output.json"
dst = Path(args.output) / "link-check-output.json"
shutil.copyfile(result_json, dst)
print(f"file generated at path: {result_json.resolve()}")
result_txt = tmpdir / "output.txt"
if sp.returncode == 1 or result_txt.read_text() != "":
Copy link
Collaborator

@ArBridgeman ArBridgeman Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe something like this
-> separate errors visually with a character like -
-> use a walrus operator in some way so that not doing read_text() twice. If you use a walrus operator then, it should be the first item in the or condition, as otherwise, it may not be set later.

Suggested change
if sp.returncode == 1 or result_txt.read_text() != "":
if errors:=result_txt.read_text().splitlines() or sp.returncode == 1:
escape_red = "\033[31m"
print(escape_rot + "errors:")
print('\n'.join(f"- {item}" for item in errors))
session.error(1)

escape_rot = "\033[31m"
print(escape_rot + "errors:")
print(result_txt.read_text())
session.error(1)


@nox.session(name="changelog:updated", python=False)
def updated(_session: Session) -> None:
"""Checks if the change log has been updated"""
Expand Down
Loading