From cd0b0db35148ed278a46494f343a8532dc212f26 Mon Sep 17 00:00:00 2001 From: Dominik Date: Tue, 23 Jan 2024 17:25:20 +0200 Subject: [PATCH 1/4] chore: minor spacing adjustments --- custom_conf.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/custom_conf.py b/custom_conf.py index 5d76b82..0b8cf77 100644 --- a/custom_conf.py +++ b/custom_conf.py @@ -113,7 +113,6 @@ # Set up redirects (https://documatt.gitlab.io/sphinx-reredirects/usage.html) # For example: 'explanation/old-name.html': '../how-to/prettify.html', - redirects = {} ############################################################ @@ -121,16 +120,13 @@ ############################################################ # Links to ignore when checking links - linkcheck_ignore = [ 'http://127.0.0.1:8000' ] # Pages on which to ignore anchors # (This list will be appended to linkcheck_anchors_ignore_for_url) - -custom_linkcheck_anchors_ignore_for_url = [ - ] +custom_linkcheck_anchors_ignore_for_url = [] ############################################################ ### Additions to default configuration @@ -148,7 +144,7 @@ # Add files or directories that should be excluded from processing. custom_excludes = [ 'doc-cheat-sheet*', -] + ] # Add CSS files (located in .sphinx/_static/) custom_html_css_files = [] From dd0ae8400b0e9f94f34cefb06bc61b8f766ed3ae Mon Sep 17 00:00:00 2001 From: Dominik Date: Tue, 23 Jan 2024 17:27:45 +0200 Subject: [PATCH 2/4] feat: optional dependency management * `.sphinx/requirements.txt` was removed and added to `.gitignore` * `make clean` deletes `.sphinx/requirements.txt` (if present) * `make install` automatically generates `.sphinx/requirements.txt` (if not present) based on the configuration specified index `custom_conf.py`: - only enables redirects extension if any redirects are specified - only enables open graph extension if any configuration is present - only enables myst extensions if any configuration is present - moved `sphinx_tabs.tabs`, `canonical.`* and `notfound.extension` to `custom_extensions` - canonical sphinx module will only be used as dependency if any canonical sphinx extension is specified in `custom_extensions` (legacy names (without "canonical." prefix) are handled correctly) - if any automatically handled extension is explicitly specified in `custom_extensions` it will be enabled even if the configuration context suggest otherwise - the resulting list of extension will be deduplicated (legacy names of canonical sphinx extension are handled correctly) Co-authored-by: Ruth Fuchss --- .gitignore | 1 + .sphinx/requirements.txt | 14 ----- Makefile | 4 +- build_requirements.py | 124 +++++++++++++++++++++++++++++++++++++++ conf.py | 40 +++++++------ custom_conf.py | 31 ++++++++-- 6 files changed, 177 insertions(+), 37 deletions(-) delete mode 100644 .sphinx/requirements.txt create mode 100644 build_requirements.py diff --git a/.gitignore b/.gitignore index 743e132..f8ad376 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /*env*/ .sphinx/venv/ +.sphinx/requirements.txt .sphinx/warnings.txt .sphinx/.wordlist.dic .sphinx/.doctrees/ diff --git a/.sphinx/requirements.txt b/.sphinx/requirements.txt deleted file mode 100644 index 0f7ffa5..0000000 --- a/.sphinx/requirements.txt +++ /dev/null @@ -1,14 +0,0 @@ -canonical-sphinx-extensions -furo -linkify-it-py -myst-parser -pyspelling -sphinx -sphinx-autobuild -sphinx-copybutton -sphinx-design -sphinx-notfound-page -sphinx-reredirects -sphinx-tabs -sphinxcontrib-jquery -sphinxext-opengraph diff --git a/Makefile b/Makefile index 876d2af..ce1a793 100644 --- a/Makefile +++ b/Makefile @@ -19,9 +19,8 @@ VENV = $(VENVDIR)/bin/activate help: $(VENVDIR) @. $(VENV); $(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -# Explicit target avoids fall-through to the "Makefile" target. $(SPHINXDIR)/requirements.txt: - test -f $(SPHINXDIR)/requirements.txt + python3 build_requirements.py # If requirements are updated, venv should be rebuilt and timestamped. $(VENVDIR): $(SPHINXDIR)/requirements.txt @@ -77,6 +76,7 @@ serve: html clean: clean-doc @test ! -e "$(VENVDIR)" -o -d "$(VENVDIR)" -a "$(abspath $(VENVDIR))" != "$(VENVDIR)" rm -rf $(VENVDIR) + rm -f .sphinx/requirements.txt clean-doc: git clean -fx "$(BUILDDIR)" diff --git a/build_requirements.py b/build_requirements.py new file mode 100644 index 0000000..1ffa019 --- /dev/null +++ b/build_requirements.py @@ -0,0 +1,124 @@ +import sys + +sys.path.append('./') +from custom_conf import * + +# The file contains helper functions and the mechanism to build the +# .sphinx/requirements.txt file that is needed to set up the virtual +# environment. + +# You should not do any modifications to this file. Put your custom +# requirements into the custom_required_modules array in the custom_conf.py +# file. If you need to change this file, contribute the changes upstream. + +legacyCanonicalSphinxExtensionNames = [ + "youtube-links", + "related-links", + "custom-rst-roles", + "terminal-output" + ] + +def IsAnyCanonicalSphinxExtensionUsed(): + for extension in custom_extensions: + if (extension.startswith("canonical.") or + extension in legacyCanonicalSphinxExtensionNames): + return True + + return False + +def IsNotFoundExtensionUsed(): + return "notfound.extension" in custom_extensions + +def IsSphinxTabsUsed(): + for extension in custom_extensions: + if extension.startswith("sphinx_tabs."): + return True + + return False + +def AreRedirectsDefined(): + return ("sphinx_reredirects" in custom_extensions) or ( + ("redirects" in globals()) and \ + (redirects is not None) and \ + (len(redirects) > 0)) + +def IsOpenGraphConfigured(): + if "sphinxext.opengraph" in custom_extensions: + return True + + for global_variable_name in list(globals()): + if global_variable_name.startswith("ogp_"): + return True + + return False + +def IsMyStParserUsed(): + return ("myst_parser" in custom_extensions) or \ + ("custom_myst_extensions" in globals()) + +def DeduplicateExtensions(extensionNames: [str]): + extensionNames = dict.fromkeys(extensionNames) + resultList = [] + encounteredCanonicalExtensions = [] + + for extensionName in extensionNames: + if extensionName in legacyCanonicalSphinxExtensionNames: + extensionName = "canonical." + extensionName + + if extensionName.startswith("canonical."): + if extensionName not in encounteredCanonicalExtensions: + encounteredCanonicalExtensions.append(extensionName) + resultList.append(extensionName) + else: + resultList.append(extensionName) + + return resultList + +if __name__ == "__main__": + requirements = [ + "furo", + "pyspelling", + "sphinx", + "sphinx-autobuild", + "sphinx-copybutton", + "sphinx-design", + "sphinxcontrib-jquery" + ] + + requirements.extend(custom_required_modules) + + if IsAnyCanonicalSphinxExtensionUsed(): + requirements.append("canonical-sphinx-extensions") + + if IsNotFoundExtensionUsed(): + requirements.append("sphinx-notfound-page") + + if IsSphinxTabsUsed(): + requirements.append("sphinx-tabs") + + if AreRedirectsDefined(): + requirements.append("sphinx-reredirects") + + if IsOpenGraphConfigured(): + requirements.append("sphinxext-opengraph") + + if IsMyStParserUsed(): + requirements.append("myst-parser") + requirements.append("linkify-it-py") + + # removes duplicate entries + requirements = list(dict.fromkeys(requirements)) + requirements.sort() + + with open(".sphinx/requirements.txt", 'w') as requirements_file: + requirements_file.write( + "# DO NOT MODIFY THIS FILE DIRECTLY!\n" + "#\n" + "# This file is generated automatically.\n" + "# Add custom requirements to the custom_required_modules\n" + "# array in the custom_conf.py file and run:\n" + "# make clean && make install\n") + + for requirement in requirements: + requirements_file.write(requirement) + requirements_file.write('\n') diff --git a/conf.py b/conf.py index b02b9fe..f7b8e44 100644 --- a/conf.py +++ b/conf.py @@ -2,6 +2,7 @@ sys.path.append('./') from custom_conf import * +from build_requirements import * # Configuration file for the Sphinx documentation builder. # You should not do any modifications to this file. Put your custom @@ -17,30 +18,35 @@ extensions = [ 'sphinx_design', - 'sphinx_tabs.tabs', - 'sphinx_reredirects', - 'canonical.youtube-links', - 'canonical.related-links', - 'canonical.custom-rst-roles', - 'canonical.terminal-output', 'sphinx_copybutton', - 'sphinxext.opengraph', - 'myst_parser', 'sphinxcontrib.jquery', - 'notfound.extension' ] + +# Only add redirects extension if any redirects are specified. +if AreRedirectsDefined(): + extensions.append('sphinx_reredirects') + +# Only add myst extensions if any configuration is present. +if IsMyStParserUsed(): + extensions.append('myst_parser') + + # Additional MyST syntax + myst_enable_extensions = [ + 'substitution', + 'deflist', + 'linkify' + ] + myst_enable_extensions.extend(custom_myst_extensions) + +# Only add Open Graph extension if any configuration is present. +if IsOpenGraphConfigured(): + extensions.append('sphinxext.opengraph') + extensions.extend(custom_extensions) +extensions = DeduplicateExtensions(extensions) ### Configuration for extensions -# Additional MyST syntax -myst_enable_extensions = [ - 'substitution', - 'deflist', - 'linkify' -] -myst_enable_extensions.extend(custom_myst_extensions) - # Used for related links if not 'discourse_prefix' in html_context and 'discourse' in html_context: html_context['discourse_prefix'] = html_context['discourse'] + '/t/' diff --git a/custom_conf.py b/custom_conf.py index 0b8cf77..c233943 100644 --- a/custom_conf.py +++ b/custom_conf.py @@ -47,6 +47,8 @@ ## when linking to the documentation from another website (see https://ogp.me/) # The URL where the documentation will be hosted (leave empty if you # don't know yet) +# NOTE: If no ogp_* variable is defined (e.g. if you remove this section) the +# sphinxext.opengraph extension will be disabled. ogp_site_url = 'https://canonical-starter-pack.readthedocs-hosted.com/' # The documentation website name (usually the same as the product name) ogp_site_name = project @@ -113,6 +115,8 @@ # Set up redirects (https://documatt.gitlab.io/sphinx-reredirects/usage.html) # For example: 'explanation/old-name.html': '../how-to/prettify.html', +# NOTE: If this variable is not defined, set to None, or the dictionary is empty, +# the sphinx_reredirects extension will be disabled. redirects = {} ############################################################ @@ -134,12 +138,31 @@ ## The following settings are appended to the default configuration. ## Use them to extend the default functionality. +# NOTE: Remove this variable to disable the MyST parser extensions. +custom_myst_extensions = [] -# Add extensions -custom_extensions = [] +# Add custom Sphinx extensions as needed. +# This array contains recommended extensions that should be used. +# NOTE: The following extensions are handled automatically and do +# not need to be added here: myst_parser, sphinx_copybutton, sphinx_design, +# sphinx_reredirects, sphinxcontrib.jquery, sphinxext.opengraph +custom_extensions = [ + 'sphinx_tabs.tabs', + 'canonical.youtube-links', + 'canonical.related-links', + 'canonical.custom-rst-roles', + 'canonical.terminal-output', + 'notfound.extension' + ] -# Add MyST extensions -custom_myst_extensions = [] +# Add custom required Python modules that must be added to the +# .sphinx/requirements.txt file. +# NOTE: The following modules are handled automatically and do not need to be +# added here: canonical-sphinx-extensions, furo, linkify-it-py, myst-parser, +# pyspelling, sphinx, sphinx-autobuild, sphinx-copybutton, sphinx-design, +# sphinx-notfound-page, sphinx-reredirects, sphinx-tabs, sphinxcontrib-jquery, +# sphinxext-opengraph +custom_required_modules = [] # Add files or directories that should be excluded from processing. custom_excludes = [ From e26f432b1fa3b936db1c5ed0c53f5869df1f5234 Mon Sep 17 00:00:00 2001 From: Dominik Date: Tue, 23 Jan 2024 19:04:49 +0200 Subject: [PATCH 3/4] ci(RTD): generate requirements.txt post checkout * `.readthedocs.yaml` instructs Read The Docs (RTD) builder to generate `.sphinx/requirements.txt` post checkout * `init.sh` script correctly updates the path where the `build_requirements.py` is located/needs to be executed Co-authored-by: Ruth Fuchss --- .readthedocs.yaml | 3 +++ init.sh | 1 + 2 files changed, 4 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index f2248a7..f41b55d 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -10,6 +10,9 @@ build: os: ubuntu-22.04 tools: python: "3.11" + jobs: + post_checkout: + - python3 build_requirements.py # Build documentation in the docs/ directory with Sphinx sphinx: diff --git a/init.sh b/init.sh index f6f0226..99b093a 100755 --- a/init.sh +++ b/init.sh @@ -16,6 +16,7 @@ rm -rf "$temp_directory/.git" echo "Updating working directory in workflow files..." sed -i "s|working-directory:\s*'\.'|working-directory: '$install_directory'|g" "$temp_directory/.github/workflows"/* echo "Updating .readthedocs.yaml configuration..." +sed -i "s|-\s\s*python3\s\s*build_requirements\.py|- cd '$install_directory' \&\& python3 build_requirements.py|g" "$temp_directory/.readthedocs.yaml" sed -i "s|configuration:\s*conf\.py|configuration: $install_directory/conf.py|g" "$temp_directory/.readthedocs.yaml" sed -i "s|requirements:\s*\.sphinx/requirements\.txt|requirements: $install_directory/.sphinx/requirements.txt|g" "$temp_directory/.readthedocs.yaml" From bf41e179bf5730f60de3afb3fbc30bb8d071110b Mon Sep 17 00:00:00 2001 From: Dominik Date: Tue, 23 Jan 2024 19:11:12 +0200 Subject: [PATCH 4/4] docs(readme): add dependency management notes Documents the added optional dependency mechanism in the `readme.rst`. Co-authored-by: Ruth Fuchss --- readme.rst | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/readme.rst b/readme.rst index 62230c8..39e16fc 100644 --- a/readme.rst +++ b/readme.rst @@ -117,6 +117,7 @@ To add documentation to an existing code repository: #. create a symbolic link to the ``docs/.wokeignore`` file from the root directory of the code repository #. in file ``docs/.readthedocs.yaml`` set the following: + * ``post_checkout: cd docs && python3 build_requirements.py`` * ``configuration: docs/conf.py`` * ``requirements: docs/.sphinx/requirements.txt`` @@ -203,13 +204,16 @@ They are pre-configured as needed, but you can customise their configuration in The following extensions are always included: - |sphinx-design|_ +- |sphinx_copybutton|_ +- |sphinxcontrib.jquery|_ + +The following extensions will automatically be included based on the configuration in the ``custom_conf.py`` file: + - |sphinx_tabs.tabs|_ - |sphinx_reredirects|_ -- |lxd-sphinx-extensions|_ (``youtube-links``, ``related-links``, ``custom-rst-roles``, and ``terminal-output``) -- |sphinx_copybutton|_ - |sphinxext.opengraph|_ +- |lxd-sphinx-extensions|_ (``youtube-links``, ``related-links``, ``custom-rst-roles``, and ``terminal-output``) - |myst_parser|_ -- |sphinxcontrib.jquery|_ - |notfound.extension|_ You can add further extensions in the ``custom_extensions`` variable in ``custom_conf.py``. @@ -253,8 +257,13 @@ To install the prerequisites: make install -This will create a virtual environment (``.sphinx/venv``) and install -dependency software (``.sphinx/requirements.txt``) within it. +This will create the required software list (``.sphinx/requirements.txt``) +which is used to create a virtual environment (``.sphinx/venv``) and install +dependency software within it. + +You can add further Python modules to the required software list +(``.sphinx/requirements.txt```) in the ``custom_required_modules`` variable +in the ``custom_conf.py`` file. **Note**: By default, the starter pack uses the latest compatible version of all tools and does not pin its requirements.