From aca2632675675c38b6907e1a3c0aab26f13fd298 Mon Sep 17 00:00:00 2001 From: Minae Lee Date: Thu, 30 Apr 2026 17:10:36 -0700 Subject: [PATCH 01/19] doc: remove use of build_requirements.py Signed-off-by: Minae Lee --- .github/workflows/tests.yml | 1 + doc/.gitignore | 1 - doc/.readthedocs.yaml | 2 +- doc/.sphinx/build_requirements.py | 127 ------------------------------ doc/Makefile.sp | 18 +---- doc/conf.py | 32 +++----- doc/requirements.txt | 37 +++++++++ 7 files changed, 52 insertions(+), 166 deletions(-) delete mode 100644 doc/.sphinx/build_requirements.py create mode 100644 doc/requirements.txt diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1b114b60e..fd85f2cb3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -297,6 +297,7 @@ jobs: with: working-directory: './doc' makefile: 'Makefile' + python-version: '3.12' snap: name: Trigger snap edge build diff --git a/doc/.gitignore b/doc/.gitignore index 1df7bfaf1..9190b4f0e 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -1,6 +1,5 @@ /*env*/ .sphinx/venv/ -.sphinx/requirements.txt .sphinx/warnings.txt .sphinx/.wordlist.dic .sphinx/.doctrees/ diff --git a/doc/.readthedocs.yaml b/doc/.readthedocs.yaml index 3df760f20..1796093af 100644 --- a/doc/.readthedocs.yaml +++ b/doc/.readthedocs.yaml @@ -35,4 +35,4 @@ sphinx: # Optionally declare the Python requirements required to build your docs python: install: - - requirements: doc/.sphinx/requirements.txt + - requirements: doc/requirements.txt diff --git a/doc/.sphinx/build_requirements.py b/doc/.sphinx/build_requirements.py deleted file mode 100644 index c9511800c..000000000 --- a/doc/.sphinx/build_requirements.py +++ /dev/null @@ -1,127 +0,0 @@ -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", - "watchfiles", - "GitPython" - - ] - - requirements.extend(custom_required_modules) - - if IsAnyCanonicalSphinxExtensionUsed(): - requirements.append("canonical-sphinx-extensions==0.0.27") - - 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==4.0.1") - 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/doc/Makefile.sp b/doc/Makefile.sp index 61c42cf82..e258a8b66 100644 --- a/doc/Makefile.sp +++ b/doc/Makefile.sp @@ -27,23 +27,12 @@ sp-full-help: $(VENVDIR) @echo "\n\033[1;31mNOTE: This help texts shows unsupported targets!\033[0m" @echo "Run 'make help' to see supported targets." -# Shouldn't assume that venv is available on Ubuntu by default; discussion here: -# https://bugs.launchpad.net/ubuntu/+source/python3.4/+bug/1290847 -$(SPHINXDIR)/requirements.txt: - @python3 -c "import venv" || \ - (echo "You must install python3-venv before you can build the documentation."; exit 1) - python3 -m venv $(VENVDIR) - @if [ ! -z "$(ADDPREREQS)" ]; then \ - . $(VENV); pip install --require-virtualenv $(ADDPREREQS); \ - fi - . $(VENV); python3 $(SPHINXDIR)/build_requirements.py - # If requirements are updated, venv should be rebuilt and timestamped. -$(VENVDIR): $(SPHINXDIR)/requirements.txt +$(VENVDIR): @echo "... setting up virtualenv" - python3 -m venv $(VENVDIR) + python3 -m venv $(VENVDIR) || { echo "You must install python3-venv before you can build the documentation."; exit 1; } . $(VENV); pip install --require-virtualenv \ - --upgrade -r $(SPHINXDIR)/requirements.txt \ + --upgrade -r requirements.txt \ --log $(VENVDIR)/pip_install.log @test ! -f $(VENVDIR)/pip_list.txt || \ mv $(VENVDIR)/pip_list.txt $(VENVDIR)/pip_list.txt.bak @@ -79,7 +68,6 @@ sp-serve: sp-html sp-clean: sp-clean-doc @test ! -e "$(VENVDIR)" -o -d "$(VENVDIR)" -a "$(abspath $(VENVDIR))" != "$(VENVDIR)" rm -rf $(VENVDIR) - rm -f $(SPHINXDIR)/requirements.txt rm -rf $(SPHINXDIR)/node_modules/ rm -rf $(SPHINXDIR)/styles rm -rf $(SPHINXDIR)/vale.ini diff --git a/doc/conf.py b/doc/conf.py index 7cdb733b6..d6a517c4f 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -8,7 +8,6 @@ sys.path.append('./') from custom_conf import * sys.path.append('.sphinx/') -from build_requirements import * # Configuration file for the Sphinx documentation builder. # You should not do any modifications to this file. Put your custom @@ -22,34 +21,23 @@ ### Extensions ############################################################ +# Custom MyST syntax extensions; see +# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html +# +# NOTE: By default, the following MyST extensions are enabled: +# substitution, deflist, linkify +myst_enable_extensions = set() + extensions = [ 'sphinx_design', 'sphinx_copybutton', 'sphinxcontrib.jquery', + 'sphinx_reredirects', + 'myst_parser', + 'sphinxext.opengraph', ] -# 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 diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 000000000..d73f292e4 --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1,37 @@ +furo +canonical-sphinx-extensions + +# Extensions previously auto-loaded by canonical-sphinx +myst-parser~=4.0 # v5.0.0 causes version conflicts +sphinx-autobuild==2025.8.25 +sphinx-design==0.6.1 +sphinx-notfound-page~=1.1 +sphinx-reredirects==0.1.6 +sphinx-tabs~=3.5 +sphinxcontrib-jquery~=4.1 +sphinxext-opengraph~=0.13 + +# Extra extensions, previously bundled as canonical-sphinx-extensions +sphinx-config-options~=0.1 +sphinx-filtered-toctree~=0.1 +sphinx-related-links~=0.1 +sphinx-roles~=0.1 +sphinx-terminal~=1.0 +sphinx-youtube-links~=0.1 + +# Other dependencies +packaging~=26.1 +pyspelling +sphinxcontrib-svg2pdfconverter[CairoSVG]~=2.1 +sphinx-last-updated-by-git~=0.3 +sphinx-sitemap~=2.9 + +# Vale dependencies +vale~=3.13 + +# MicroCloud custom dependencies +gitpython==3.1.49 +linkify-it-py==2.0.3 +pyyaml==6.0.3 +sphinx-copybutton==0.5.2 +watchfiles==1.1.1 From 0192bce57e615f39c21f19ed9a3ed9e9a1fa280b Mon Sep 17 00:00:00 2001 From: Minae Lee Date: Fri, 1 May 2026 14:35:03 -0700 Subject: [PATCH 02/19] doc: merge conf.py and custom_conf.py Moves code from custom_conf.py into conf.py and aligns it with the Sphinx Stack (formerly the Sphinx Starter Pack). This deliberately excludes the code for the Swagger UI, which will be added into conf.py in a separate commit to make this change easier to backport to the v2-edge branch, which does not use Swagger UI. Signed-off-by: Minae Lee --- doc/conf.py | 395 ++++++++++++++++++++++++++++----------------- doc/custom_conf.py | 358 ---------------------------------------- 2 files changed, 248 insertions(+), 505 deletions(-) delete mode 100644 doc/custom_conf.py diff --git a/doc/conf.py b/doc/conf.py index d6a517c4f..dff6c6575 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,124 +1,230 @@ -import sys +import datetime import os -import requests -from urllib.parse import urlparse -from git import Repo, InvalidGitRepositoryError -import time +import yaml + +############################ +# MicroCloud custom configuration # +############################ + +import sys +from git import Repo +import re sys.path.append('./') -from custom_conf import * sys.path.append('.sphinx/') -# Configuration file for the Sphinx documentation builder. -# You should not do any modifications to this file. Put your custom -# configuration into the custom_conf.py file. -# If you need to change this file, contribute the changes upstream. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html +from redirects import redirects -############################################################ -### Extensions -############################################################ +with open('../version/version.go') as f: + match = re.search(r'RawVersion = "([^"]+)"', f.read()) + version = match.group(1) if match else '' -# Custom MyST syntax extensions; see -# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html -# -# NOTE: By default, the following MyST extensions are enabled: -# substitution, deflist, linkify -myst_enable_extensions = set() -extensions = [ - 'sphinx_design', - 'sphinx_copybutton', - 'sphinxcontrib.jquery', - 'sphinx_reredirects', - 'myst_parser', - 'sphinxext.opengraph', -] +####################### +# Project information # +####################### -extensions.extend(custom_extensions) +project = 'MicroCloud' +author = "Canonical Ltd." -### Configuration for extensions +# Sidebar documentation title; best kept reasonably short +# To disable the title, set to an empty string. +html_title = project + ' documentation ' + version -# Used for related links -if not 'discourse_prefix' in html_context and 'discourse' in html_context: - html_context['discourse_prefix'] = html_context['discourse'] + '/t/' +# Copyright string; shown at the bottom of the page +copyright = '2014-%s AGPL-3.0, %s' % (datetime.date.today().year, author) -# The URL prefix for the notfound extension depends on whether the documentation uses versions. -# For documentation on documentation.ubuntu.com, we also must add the slug. -url_version = '' -url_lang = '' +# Use RTD canonical URL to ensure duplicate pages have a single canonical URL +# that includes the version (such as /latest/); helps SEO. +html_baseurl = os.environ.get('READTHEDOCS_CANONICAL_URL', '/') -# Determine if the URL uses versions and language -if 'READTHEDOCS_CANONICAL_URL' in os.environ and os.environ['READTHEDOCS_CANONICAL_URL']: - url_parts = os.environ['READTHEDOCS_CANONICAL_URL'].split('/') +# OpenGraph metadata used for social sharing previews +ogp_site_url = html_baseurl +ogp_site_name = html_title +ogp_image = 'https://documentation.ubuntu.com/microcloud/latest/_static/tag.png' - if len(url_parts) >= 2 and 'READTHEDOCS_VERSION' in os.environ and os.environ['READTHEDOCS_VERSION'] == url_parts[-2]: - url_version = url_parts[-2] + '/' +html_favicon = '.sphinx/_static/favicon.png' - if len(url_parts) >= 3 and 'READTHEDOCS_LANGUAGE' in os.environ and os.environ['READTHEDOCS_LANGUAGE'] == url_parts[-3]: - url_lang = url_parts[-3] + '/' +html_context = { + 'product_page': 'canonical.com/microcloud', + 'product_tag': '_static/tag.png', -# Set notfound_urls_prefix to the slug (if defined) and the version/language affix -if slug: - notfound_urls_prefix = '/' + slug + '/' + url_lang + url_version -elif len(url_lang + url_version) > 0: - notfound_urls_prefix = '/' + url_lang + url_version -else: - notfound_urls_prefix = '' + 'discourse': 'https://discourse.ubuntu.com/c/lxd/microcloud/', + 'discourse_prefix': { + 'ubuntu': 'https://discourse.ubuntu.com/t/', + 'lxc': 'https://discuss.linuxcontainers.org/t/', + }, -notfound_context = { - 'title': 'Page not found', - 'body': '

Sorry, but the documentation page that you are looking for was not found.

\n\n

Documentation changes over time, and pages are moved around. We try to redirect you to the updated content where possible, but unfortunately, that didn\'t work this time (maybe because the content you were looking for does not exist in this version of the documentation).

\n

You can try to use the navigation to locate the content you\'re looking for, or search for a similar page.

\n', + 'mattermost': '', + 'matrix': '', + + 'github_url': 'https://github.com/canonical/microcloud', + 'repo_default_branch': 'main', + 'repo_folder': '/doc/', + + # Required for feedback button + 'github_issues': 'enabled', + + # Enables listing contributors on individual pages + # This feature is deprecated and will be removed in the future + 'display_contributors': False, + + 'sequential_nav': "both", } -# Default image for OGP (to prevent font errors, see -# https://github.com/canonical/sphinx-docs-starter-pack/pull/54 ) -if not 'ogp_image' in locals(): - ogp_image = 'https://assets.ubuntu.com/v1/253da317-image-document-ubuntudocs.svg' +html_extra_path = ['_extra'] -############################################################ -### General configuration -############################################################ +# Enables the pencil icon to edit pages on GitHub, shown at the top of each page +html_theme_options = { + 'source_edit_link': html_context['github_url'] +} + +# Project slug +# Required if your project is hosted on documentation.ubuntu.com +slug = "microcloud" + +####################### +# Sitemap configuration: https://sphinx-sitemap.readthedocs.io/ +####################### + +# sphinx-sitemap uses html_baseurl (set earlier in this file) to generate the full URL for each page: + +sitemap_url_scheme = '{link}' + +# Include `lastmod` dates in the sitemap: +sitemap_show_lastmod = True + +# Exclude generated pages from the sitemap: +sitemap_excludes = [ + '404/', + 'genindex/', + 'search/', +] + +# Add more pages to sitemap_excludes if needed. Wildcards are supported. +# For example, to exclude module pages generated by autodoc, add '_modules/*'. + +####################### +# Template and asset locations +####################### + +html_static_path = ['.sphinx/_static', '_static'] + + +############# +# Redirects # +############# + +# minae: Redirects is imported from redirects.py + +########################### +# Link checker exceptions # +########################### + +# Always ignore these links +linkcheck_ignore = [ + 'http://127.0.0.1:8000', + 'http://localhost:8000', + # These links may fail from time to time + 'https://ceph.io', + # Cloudflare protection on SourceForge domains often block linkcheck + r"https://.*\.sourceforge\.(net|io)/.*", + # Ignore so that we can link change log in release notes before a release is ready + r"https://github\.com/canonical/microcloud/compare.*", +] + +# Pages on which to ignore anchors (check the link without the anchor) +linkcheck_anchors_ignore_for_url = [ + r'https://snapcraft\.io/docs/.*', + r'https://github\.com/.*', + r'https://charmhub\.io/.*', +] + +# Increase linkcheck rate limit timeout max, default when unset is 300 +# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-linkcheck_timeout +linkcheck_rate_limit_timeout = 600 +# Increase linkcheck retries, default when unset is 1 +# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-linkcheck_retries +linkcheck_retries = 3 + +# Increase the duration, in seconds, that the linkcheck builder will wait for a response after each hyperlink request. +# Default when unset is 30 +# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-linkcheck_timeout +linkcheck_timeout = 45 + + +######################## +# Configuration extras # +######################## + +# Additional MyST extensions, if any +myst_enable_extensions = [ + 'substitution', + 'deflist', + 'linkify' +] + +extensions = [ + 'sphinx_design', + 'sphinx_copybutton', + 'sphinxcontrib.jquery', + 'sphinx_reredirects', + 'myst_parser', + 'sphinxext.opengraph', + 'sphinx_tabs.tabs', + 'canonical.youtube-links', + 'canonical.related-links', + 'canonical.custom-rst-roles', + 'canonical.terminal-output', + 'notfound.extension', + 'sphinx.ext.intersphinx', + 'sphinx_sitemap', +] + +# Excludes files or directories from processing exclude_patterns = [ '_build', 'Thumbs.db', '.DS_Store', '.sphinx', 'reference/release-notes/release-notes-template.md', + "integration", + 'README.md', ] -exclude_patterns.extend(custom_excludes) -rst_epilog = ''' -.. include:: /reuse/links.txt -''' -if 'custom_rst_epilog' in locals(): - rst_epilog = custom_rst_epilog +html_css_files = [ + 'custom.css', + 'header.css', + 'github_issue_links.css', + 'furo_colors.css', + 'footer.css', + 'https://assets.ubuntu.com/v1/d86746ef-cookie_banner.css', +] -source_suffix = { - '.rst': 'restructuredtext', - '.md': 'markdown', -} +html_js_files = [ + 'header-nav.js', + 'footer.js', + 'https://assets.ubuntu.com/v1/287a5e8f-bundle.js', + 'rtd-versions-flyout.js', + 'github_issue_links.js', +] -if not 'conf_py_path' in html_context and 'github_folder' in html_context: - html_context['conf_py_path'] = html_context['github_folder'] +# Feedback button at the top; enabled by default +# To disable the button, uncomment the line below: -# For ignoring specific links -linkcheck_anchors_ignore_for_url = [ - r'https://github\.com/.*', - r'https://charmhub\.io/.*', -] -linkcheck_anchors_ignore_for_url.extend(custom_linkcheck_anchors_ignore_for_url) +# disable_feedback_button = True -# Tags cannot be added directly in custom_conf.py, so add them here -for tag in custom_tags: - tags.add(tag) +# Define a :center: role that can be used to center the content of table cells. +rst_prolog = ''' +.. role:: center + :class: align-center +''' -# html_context['get_contribs'] is a function and cannot be -# cached (see https://github.com/sphinx-doc/sphinx/issues/12300) -suppress_warnings = ["config.cache"] +# Load substitutions from YAML file +if os.path.exists('./substitutions.yaml'): + with open('./substitutions.yaml', 'r') as fd: + myst_substitutions = yaml.safe_load(fd.read()) ############################################################ ### Styling @@ -132,8 +238,8 @@ # Setting templates_path for epub makes the build fail if builder == 'dirhtml' or builder == 'html': templates_path = ['.sphinx/_templates'] - if 'custom_templates_path' in globals(): - templates_path = custom_templates_path + templates_path + if os.environ.get('SINGLE_BUILD') != 'True': + templates_path.insert(0, 'integration/microcloud/_templates') notfound_template = '404.html' # Theme configuration @@ -146,64 +252,59 @@ 'sidebar_hide_name': True } -############################################################ -### Additional files -############################################################ -html_extra_path = ['_extra'] +########################################## +### Misc MicroCloud custom configuration # +########################################## -html_static_path = ['.sphinx/_static', '_static'] -if 'custom_html_static_path' in globals(): - html_static_path = html_static_path + custom_html_static_path +# Use custom 404 page text +notfound_context = { + 'title': 'Page not found', + 'body': '

Sorry, but the documentation page that you are looking for was not found.

\n\n

Documentation changes over time, and pages are moved around. We try to redirect you to the updated content where possible, but unfortunately, that didn\'t work this time (maybe because the content you were looking for does not exist in this version of the documentation).

\n

You can try to use the navigation to locate the content you\'re looking for, or search for a similar page.

\n', +} -html_css_files = [ - 'custom.css', - 'header.css', - 'github_issue_links.css', - 'furo_colors.css', - 'footer.css' -] -html_css_files.extend(custom_html_css_files) - -html_js_files = ['header-nav.js', 'footer.js'] -if 'github_issues' in html_context and html_context['github_issues'] and not disable_feedback_button: - html_js_files.append('github_issue_links.js') -html_js_files.extend(custom_html_js_files) - -############################################################# -# Display the contributors - -def get_contributors_for_file(github_url, github_folder, pagename, page_source_suffix, display_contributors_since=None): - filename = f"{pagename}{page_source_suffix}" - paths=html_context['github_folder'][1:] + filename - - try: - repo = Repo(".") - except InvalidGitRepositoryError: - cwd = os.getcwd() - ghfolder = html_context['github_folder'][:-1] - if ghfolder and cwd.endswith(ghfolder): - repo = Repo(cwd.rpartition(ghfolder)[0]) - else: - print("The local Git repository could not be found.") - return - - since = display_contributors_since if display_contributors_since and display_contributors_since.strip() else None - - commits = repo.iter_commits(paths=paths, since=since) - - contributors_dict = {} - for commit in commits: - contributor = commit.author.name - if contributor not in contributors_dict or commit.committed_date > contributors_dict[contributor]['date']: - contributors_dict[contributor] = { - 'date': commit.committed_date, - 'sha': commit.hexsha - } - # The github_page contains the link to the contributor's latest commit. - contributors_list = [{'name': name, 'github_page': f"{github_url}/commit/{data['sha']}"} for name, data in contributors_dict.items()] - sorted_contributors_list = sorted(contributors_list, key=lambda x: x['name']) - return sorted_contributors_list - -html_context['get_contribs'] = get_contributors_for_file -############################################################# +# Intersphinx setup differs for different builds to optimize use with integrated docs +if ('SINGLE_BUILD' in os.environ and os.environ['SINGLE_BUILD'] == 'True'): + intersphinx_mapping = { + 'lxd': ('https://documentation.ubuntu.com/lxd/latest/', None), + 'microceph': ('https://canonical-microceph.readthedocs-hosted.com/en/latest/', None), + 'microovn': ('https://canonical-microovn.readthedocs-hosted.com/en/latest/', None), + } +elif ('READTHEDOCS' in os.environ) and (os.environ['READTHEDOCS'] == 'True'): + intersphinx_mapping = { + 'lxd': (os.environ['PATH_PREFIX'] + 'lxd/', os.environ['READTHEDOCS_OUTPUT'] + 'html/lxd/objects.inv'), + 'microceph': (os.environ['PATH_PREFIX'] + 'microceph/', os.environ['READTHEDOCS_OUTPUT'] + 'html/microceph/objects.inv'), + 'microovn': (os.environ['PATH_PREFIX'] + 'microovn/', os.environ['READTHEDOCS_OUTPUT'] + 'html/microovn/objects.inv'), + } +else: + intersphinx_mapping = { + 'lxd': ('/lxd/', '_build/lxd/objects.inv'), + 'microceph': ('/microceph/', '_build/microceph/objects.inv'), + 'microovn': ('/microovn/', '_build/microovn/objects.inv'), + } + +# Add intersphinx mappings for docs sets not part of the MicroCloud integrated docs here: + +base_intersphinx = { + 'ceph': ('https://docs.ceph.com/en/latest/', None), + 'snap': ('https://snapcraft.io/docs/', None), +} + +intersphinx_mapping.update(base_intersphinx) + +# Update html_context for integrated builds and add the "integrated" tag +if os.environ.get('SINGLE_BUILD') != 'True': + exec(compile(source=open('.sphinx/_integration/add_config.py').read(), filename='.sphinx/_integration/add_config.py', mode='exec')) + # MicroCloud docs are at the URL root, so override the relative paths to sibling doc sets + html_context['lxd_path'] = "lxd" + html_context['lxd_tag'] = "lxd/_static/tag.png" + html_context['microceph_path'] = "microceph" + html_context['microceph_tag'] = "microceph/_static/tag.png" + html_context['microovn_path'] = "microovn" + html_context['microovn_tag'] = "microovn/_static/microovn.png" + html_static_path.append('integration/microcloud/_static') + tags.add('integrated') + +# Version label shown in the RTD flyout next to "default", in parentheses. +# Set the FLYOUT_DEFAULT_VERSION_LABEL environment variable in the RTD project dashboard. +html_context['flyout_default_version_label'] = os.environ.get('FLYOUT_DEFAULT_VERSION_LABEL', '') \ No newline at end of file diff --git a/doc/custom_conf.py b/doc/custom_conf.py deleted file mode 100644 index becc1ebca..000000000 --- a/doc/custom_conf.py +++ /dev/null @@ -1,358 +0,0 @@ -import datetime -import os -import re -import yaml -from redirects import redirects -from git import Repo, InvalidGitRepositoryError - -# Custom configuration for the Sphinx documentation builder. -# All configuration specific to your project should be done in this file. -# -# The file is included in the common conf.py configuration file. -# You can modify any of the settings below or add any configuration that -# is not covered by the common conf.py file. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html -# -# If you're not familiar with Sphinx and don't want to use advanced -# features, it is sufficient to update the settings in the "Project -# information" section. - -############################################################ -### Project information -############################################################ - -# Product name -project = 'MicroCloud' -author = 'Canonical Group Ltd' - -with open('../version/version.go') as f: - match = re.search(r'RawVersion = "([^"]+)"', f.read()) - version = match.group(1) if match else '' - -# Sidebar documentation title; best kept reasonably short -# To disable the title, set to an empty string. -html_title = project + ' documentation ' + version - -# The default value uses the current year as the copyright year. -# -# For static works, it is common to provide the year of first publication. -# Another option is to give the first year and the current year -# for documentation that is often changed, e.g. 2022–2023 (note the en-dash). -# -# A way to check a GitHub repo's creation date is to obtain a classic GitHub -# token with 'repo' permissions here: https://github.com/settings/tokens -# Next, use 'curl' and 'jq' to extract the date from the GitHub API's output: -# -# curl -H 'Authorization: token ' \ -# -H 'Accept: application/vnd.github.v3.raw' \ -# https://api.github.com/repos/canonical/ | jq '.created_at' - -copyright = '%s, %s' % (datetime.date.today().year, author) - -## Open Graph configuration - defines what is displayed as a link preview -## 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://documentation.ubuntu.com/microcloud/latest' -# The documentation website name (usually the same as the product name) -ogp_site_name = project -# The URL of an image or logo that is used in the preview -ogp_image = 'https://assets.ubuntu.com/v1/d53eeeac-Microcloud_favicon_64px_v2.png' - -# Update with the local path to the favicon for your product -# (default is the circle of friends) -html_favicon = '.sphinx/_static/favicon.png' - -# (Some settings must be part of the html_context dictionary, while others -# are on root level. Don't move the settings.) -html_context = { - - # Change to the link to the website of your product (without "https://") - # For example: "ubuntu.com/lxd" or "microcloud.is" - # If there is no product website, edit the header template to remove the - # link (see the readme for instructions). - 'product_page': 'canonical.com/microcloud', - - # Add your product tag (the orange part of your logo, will be used in the - # header) to ".sphinx/_static" and change the path here (start with "_static") - # (default is the circle of friends) - 'product_tag': '_static/tag.png', - - # Change to the discourse instance you want to be able to link to - # using the :discourse: metadata at the top of a file - # (use an empty value if you don't want to link) - 'discourse': 'https://discourse.ubuntu.com/c/lxd/microcloud/', - - # ru-fu: we're using different Discourses - 'discourse_prefix': { - 'ubuntu': 'https://discourse.ubuntu.com/t/', - 'lxc': 'https://discuss.linuxcontainers.org/t/', - }, - - # Change to the Mattermost channel you want to link to - # (use an empty value if you don't want to link) - 'mattermost': '', - - # Change to the Matrix channel you want to link to - # (use an empty value if you don't want to link) - 'matrix': '', - - # Change to the GitHub URL for your project - # This is used, for example, to link to the source files and allow creating GitHub issues directly from the documentation. - 'github_url': 'https://github.com/canonical/microcloud', - - # Change to the branch for this version of the documentation - 'github_version': 'main', - - # Change to the folder that contains the documentation - # (usually "/" or "/docs/") - 'github_folder': '/doc/', - - # Change to an empty value if your GitHub repo doesn't have issues enabled. - # This will disable the feedback button and the issue link in the footer. - 'github_issues': 'enabled', - - # Controls the existence of Previous / Next buttons at the bottom of pages - # Valid options: none, prev, next, both - 'sequential_nav': "both", - - # Controls if to display the contributors of a file or not - "display_contributors": True, - - # Controls time frame for showing the contributors - "display_contributors_since": "" -} - -# If your project is on documentation.ubuntu.com, specify the project -# slug (for example, "lxd") here. -slug = "microcloud" - -####################### -# Sitemap configuration: https://sphinx-sitemap.readthedocs.io/ -####################### - -# Base URL of RTD hosted project - -html_baseurl = 'https://documentation.ubuntu.com/microcloud/' - -# Configures URL scheme for sphinx-sitemap to generate correct URLs -# based on the version if built in RTD -if 'READTHEDOCS_VERSION' in os.environ: - rtd_version = os.environ["READTHEDOCS_VERSION"] - sitemap_url_scheme = f'{rtd_version}/{{link}}' -else: - sitemap_url_scheme = '{link}' - -############################################################ -### Redirects -############################################################ - -# Set up redirects (https://documatt.gitlab.io/sphinx-reredirects/usage.html) -# For example: 'explanation/old-name.html': '../how-to/prettify.html', -# You can also configure redirects in the Read the Docs project dashboard -# (see https://docs.readthedocs.io/en/stable/guides/redirects.html). -# NOTE: If this variable is not defined, set to None, or the dictionary is empty, -# the sphinx_reredirects extension will be disabled. -# minae: Redirects is imported from redirects.py - -############################################################ -### Link checker -############################################################ - -# Links to ignore when checking links -linkcheck_ignore = [ - 'http://127.0.0.1:8000', - 'http://localhost:8000', - # These links may fail from time to time - 'https://ceph.io', - # Cloudflare protection on SourceForge domains often block linkcheck - r"https://.*\.sourceforge\.(net|io)/.*", - # Ignore so that we can link change log in release notes before a release is ready - r"https://github\.com/canonical/microcloud/compare.*", - ] - -# Pages on which to ignore anchors -# (This list will be appended to linkcheck_anchors_ignore_for_url) - -custom_linkcheck_anchors_ignore_for_url = [ - r'https://snapcraft\.io/docs/.*' - ] - -# Increase linkcheck rate limit timeout max, default when unset is 300 -# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-linkcheck_timeout -linkcheck_rate_limit_timeout = 600 - -# Increase linkcheck retries, default when unset is 1 -# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-linkcheck_retries -linkcheck_retries = 3 - -# Increase the duration, in seconds, that the linkcheck builder will wait for a response after each hyperlink request. -# Default when unset is 30 -# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-linkcheck_timeout -linkcheck_timeout = 45 - -############################################################ -### Additions to default configuration -############################################################ - -## The following settings are appended to the default configuration. -## Use them to extend the default functionality. - -# Remove this variable to disable the MyST parser extensions. -custom_myst_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', - 'sphinx.ext.intersphinx', - 'sphinx_sitemap', - ] - -# 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 = [ - 'sphinx-sitemap', - 'pyyaml', -] - -# Add files or directories that should be excluded from processing. -custom_excludes = [ - "integration", - 'README.md' - ] - -# Add CSS files (located in .sphinx/_static/ or from external link) -custom_html_css_files = [ - 'https://assets.ubuntu.com/v1/d86746ef-cookie_banner.css', -] - -# Add JavaScript files (located in .sphinx/_static/ or from external link) -custom_html_js_files = [ - 'https://assets.ubuntu.com/v1/287a5e8f-bundle.js', - 'rtd-versions-flyout.js', -] - -## The following settings override the default configuration. - -# Specify a reST string that is included at the end of each file. -# If commented out, use the default (which pulls the reuse/links.txt -# file into each reST file). -# custom_rst_epilog = '' - -# By default, the documentation includes a feedback button at the top. -# You can disable it by setting the following configuration to True. -disable_feedback_button = False - -# Add tags that you want to use for conditional inclusion of text -# (https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#tags) -custom_tags = [] - -# If you are using the :manpage: role, set this variable to the URL for the version -# that you want to link to: -# manpages_url = "https://manpages.ubuntu.com/manpages/noble/en/man{section}/{page}.{section}.html" - -############################################################ -### Additional configuration -############################################################ - -## Add any configuration that is not covered by the common conf.py file. - -if ('SINGLE_BUILD' in os.environ and os.environ['SINGLE_BUILD'] == 'True'): - intersphinx_mapping = { - 'lxd': ('https://documentation.ubuntu.com/lxd/latest/', None), - 'microceph': ('https://canonical-microceph.readthedocs-hosted.com/en/latest/', None), - 'microovn': ('https://canonical-microovn.readthedocs-hosted.com/en/latest/', None), - } -elif ('READTHEDOCS' in os.environ) and (os.environ['READTHEDOCS'] == 'True'): - intersphinx_mapping = { - 'lxd': (os.environ['PATH_PREFIX'] + 'lxd/', os.environ['READTHEDOCS_OUTPUT'] + 'html/lxd/objects.inv'), - 'microceph': (os.environ['PATH_PREFIX'] + 'microceph/', os.environ['READTHEDOCS_OUTPUT'] + 'html/microceph/objects.inv'), - 'microovn': (os.environ['PATH_PREFIX'] + 'microovn/', os.environ['READTHEDOCS_OUTPUT'] + 'html/microovn/objects.inv'), - } -else: - intersphinx_mapping = { - 'lxd': ('/lxd/', '_build/lxd/objects.inv'), - 'microceph': ('/microceph/', '_build/microceph/objects.inv'), - 'microovn': ('/microovn/', '_build/microovn/objects.inv'), - } - -# Add intersphinx mappings for docs sets not part of the MicroCloud integrated docs here: - -base_intersphinx = { - 'ceph': ('https://docs.ceph.com/en/latest/', None), - 'snap': ('https://snapcraft.io/docs/', None), -} - -intersphinx_mapping.update(base_intersphinx) - -# Define a :center: role that can be used to center the content of table cells. -rst_prolog = ''' -.. role:: center - :class: align-center -''' - -if not ('SINGLE_BUILD' in os.environ and os.environ['SINGLE_BUILD'] == 'True'): - exec(compile(source=open('.sphinx/_integration/add_config.py').read(), filename='.sphinx/_integration/add_config.py', mode='exec')) - # MicroCloud docs are at the URL root, so override the relative paths to sibling doc sets - html_context['lxd_path'] = "lxd" - html_context['lxd_tag'] = "lxd/_static/tag.png" - html_context['microceph_path'] = "microceph" - html_context['microceph_tag'] = "microceph/_static/tag.png" - html_context['microovn_path'] = "microovn" - html_context['microovn_tag'] = "microovn/_static/microovn.png" - custom_html_static_path = ['integration/microcloud/_static'] - custom_templates_path = ['integration/microcloud/_templates'] - custom_tags.append('integrated') - -# Load substitutions from YAML file -if os.path.exists('./substitutions.yaml'): - with open('./substitutions.yaml', 'r') as fd: - myst_substitutions = yaml.safe_load(fd.read()) - -# Version label shown in the RTD flyout next to "default", in parentheses. -# Set the FLYOUT_DEFAULT_VERSION_LABEL environment variable in the RTD project dashboard. -html_context['flyout_default_version_label'] = os.environ.get('FLYOUT_DEFAULT_VERSION_LABEL', '') - -# SwaggerUI configuration - -if os.environ.get('READTHEDOCS'): - swagger_url_scheme = '/microcloud/latest/api/#{{path}}' -else: - swagger_url_scheme = '/api/#{{path}}' - -myst_url_schemes = { - 'http': None, - 'https': None, - 'swagger': swagger_url_scheme, -} - -# Download and link swagger-ui files -if not os.path.isdir('.sphinx/deps/swagger-ui'): - Repo.clone_from('https://github.com/swagger-api/swagger-ui', '.sphinx/deps/swagger-ui', depth=1) - -os.makedirs('_static/swagger-ui/', exist_ok=True) - -if not os.path.islink('_static/swagger-ui/swagger-ui-bundle.js'): - os.symlink('../../.sphinx/deps/swagger-ui/dist/swagger-ui-bundle.js', '_static/swagger-ui/swagger-ui-bundle.js') -if not os.path.islink('_static/swagger-ui/swagger-ui-standalone-preset.js'): - os.symlink('../../.sphinx/deps/swagger-ui/dist/swagger-ui-standalone-preset.js', '_static/swagger-ui/swagger-ui-standalone-preset.js') -if not os.path.islink('_static/swagger-ui/swagger-ui.css'): - os.symlink('../../.sphinx/deps/swagger-ui/dist/swagger-ui.css', '_static/swagger-ui/swagger-ui.css') From 60dece9d0c29adb88eaea215bf62c5f2c346fc6b Mon Sep 17 00:00:00 2001 From: Minae Lee Date: Thu, 7 May 2026 09:51:39 -0700 Subject: [PATCH 03/19] doc: add swagger-ui to conf.py Signed-off-by: Minae Lee --- doc/conf.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/doc/conf.py b/doc/conf.py index dff6c6575..aa6e64efa 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -305,6 +305,32 @@ html_static_path.append('integration/microcloud/_static') tags.add('integrated') +# SwaggerUI configuration +if os.environ.get('READTHEDOCS'): + swagger_url_scheme = '/microcloud/latest/api/#{{path}}' +else: + swagger_url_scheme = '/api/#{{path}}' + +myst_url_schemes = { + 'http': None, + 'https': None, + 'swagger': swagger_url_scheme, +} + +# Download and link swagger-ui files +if not os.path.isdir('.sphinx/deps/swagger-ui'): + Repo.clone_from('https://github.com/swagger-api/swagger-ui', '.sphinx/deps/swagger-ui', depth=1) + +os.makedirs('_static/swagger-ui/', exist_ok=True) + +if not os.path.islink('_static/swagger-ui/swagger-ui-bundle.js'): + os.symlink('../../.sphinx/deps/swagger-ui/dist/swagger-ui-bundle.js', '_static/swagger-ui/swagger-ui-bundle.js') +if not os.path.islink('_static/swagger-ui/swagger-ui-standalone-preset.js'): + os.symlink('../../.sphinx/deps/swagger-ui/dist/swagger-ui-standalone-preset.js', '_static/swagger-ui/swagger-ui-standalone-preset.js') +if not os.path.islink('_static/swagger-ui/swagger-ui.css'): + os.symlink('../../.sphinx/deps/swagger-ui/dist/swagger-ui.css', '_static/swagger-ui/swagger-ui.css') + + # Version label shown in the RTD flyout next to "default", in parentheses. # Set the FLYOUT_DEFAULT_VERSION_LABEL environment variable in the RTD project dashboard. html_context['flyout_default_version_label'] = os.environ.get('FLYOUT_DEFAULT_VERSION_LABEL', '') \ No newline at end of file From d0e63d651cd24c938274fe580f1ede9178608517 Mon Sep 17 00:00:00 2001 From: Minae Lee Date: Mon, 4 May 2026 14:03:25 -0700 Subject: [PATCH 04/19] doc: merge and update makefile Merge Makefile.sp into Makefile and update Makefile to better align with the Sphinx Stack (formerly the Sphinx Starter Pack). Signed-off-by: Minae Lee --- doc/.gitignore | 2 +- doc/.readthedocs.yaml | 2 +- doc/Makefile | 188 +++++++++++++++++++++++++++++------------- doc/Makefile.sp | 109 ------------------------ doc/conf.py | 1 + 5 files changed, 132 insertions(+), 170 deletions(-) delete mode 100644 doc/Makefile.sp diff --git a/doc/.gitignore b/doc/.gitignore index 9190b4f0e..3fd1e898a 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -1,5 +1,5 @@ /*env*/ -.sphinx/venv/ +.venv/ .sphinx/warnings.txt .sphinx/.wordlist.dic .sphinx/.doctrees/ diff --git a/doc/.readthedocs.yaml b/doc/.readthedocs.yaml index 1796093af..20abf42cf 100644 --- a/doc/.readthedocs.yaml +++ b/doc/.readthedocs.yaml @@ -20,7 +20,7 @@ build: # and https://github.com/canonical/microceph/pull/400 is restored) - ln -s /bin/true doc/integration/microovn/docs/woke - ln -s /bin/true doc/integration/microceph/docs/woke - - make GOTOOLCHAIN=auto doc-html-rtd PATH=$PATH:. + - cd doc && make GOTOOLCHAIN=auto html-rtd PATH=$PATH:. # Build documentation in the docs/ directory with Sphinx sphinx: diff --git a/doc/Makefile b/doc/Makefile index 014d0e5ef..6f4359de0 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,12 +1,3 @@ -# This Makefile stub allows you to customize starter pack (SP) targets. -# Consider this file as a bridge between your project -# and the starter pack's predefined targets that reside in Makefile.sp. -# -# You can add your own, non-SP targets here or override SP targets -# to fit your project's needs. For example, you can define and use targets -# named "install" or "run", but continue to use SP targets like "sp-install" -# or "sp-run" when working on the documentation. - current_dir := $(dir $(abspath $(firstword $(MAKEFILE_LIST)))) # Set the path prefix and MicroCloud component versions corresponding to each MicroCloud docs version @@ -16,30 +7,78 @@ MICROCEPHVERSION = origin/main MICROOVNVERSION = origin/main SPHINX_DIR ?= .sphinx -DOCS_SOURCEDIR ?= . -DOCS_VENVDIR ?= $(SPHINX_DIR)/venv +SPHINX_OPTS ?= -c . -d $(SPHINX_DIR)/.doctrees -j auto +SPHINX_BUILD ?= $(DOCS_VENVDIR)/bin/sphinx-build +SPHINX_HOST ?= 127.0.0.1 +SPHINX_PORT ?= 8000 +DOCS_VENVDIR ?= .venv DOCS_VENV ?= $(DOCS_VENVDIR)/bin/activate -INTEGRATION_DIR ?= integration +DOCS_SOURCEDIR ?= . +DOCS_BUILDDIR ?= _build + +PA11Y_CMD ?= $(SPHINX_DIR)/node_modules/pa11y/bin/pa11y.js --config $(SPHINX_DIR)/pa11y.json +TARGET = * +ALLFILES = *.rst **/*.rst +INTEGRATION_DIR ?= integration # Put it first so that "make" without argument is like "make help". help: - @echo "\n" \ - "------------------------------------------------------------- \n" \ - "* watch, build and serve the documentation: make run \n" \ - "* only build: make html \n" \ - "* only serve: make serve \n" \ - "* clean built doc files: make clean-doc \n" \ - "* clean full environment: make clean \n" \ - "* check links: make linkcheck \n" \ - "* check spelling: make spelling \n" \ - "* check spelling (without building again): make spellcheck \n" \ - "* check inclusive language: make woke \n" \ - "* check accessibility: make pa11y \n" \ - "* check style guide compliance: make vale \n" \ - "* check style guide compliance on target: make vale TARGET=* \n" \ - "* check markdown: make lint-md \n" \ - "* other possible targets: make \n" \ - "------------------------------------------------------------- \n" + @echo + @echo "-------------------------------------------------------------" + @echo "* watch, build and serve the documentation: make run" + @echo "* only build: make html" + @echo "* only serve: make serve" + @echo "* clean built doc files: make clean-doc" + @echo "* clean full environment: make clean" + @echo "* check links: make linkcheck" + @echo "* check markdown: make lint-md" + @echo "* check spelling: make spelling" + @echo "* check spelling (without building again): make spellcheck" + @echo "* check inclusive language: make woke" + @echo "* check accessibility: make pa11y" + @echo "* check style guide compliance: make vale" + @echo "* check style guide compliance on target: make vale TARGET=*" + @echo "* other possible targets: make " + @echo "-------------------------------------------------------------" + @echo + +.PHONY: help full-help html epub linkcheck spelling spellcheck woke \ + vale pa11y run serve install pa11y-install \ + clean clean-doc lint-md lint \ + integrate clean-integrate html html-rtd microcloud \ + serve-microcloud woke-install pymarkdownlnt-install + +full-help: $(DOCS_VENVDIR) + @. $(DOCS_VENV); $(SPHINX_BUILD) -M help "$(DOCS_SOURCEDIR)" "$(DOCS_BUILDDIR)" $(SPHINX_OPTS) $(O) + @echo "\n\033[1;31mNOTE: This help texts shows unsupported targets!\033[0m" + @echo "Run 'make help' to see supported targets." + +# If requirements are updated, venv should be rebuilt and timestamped. +$(DOCS_VENVDIR): + @echo "... setting up virtualenv" + python3 -m venv $(DOCS_VENVDIR) || { echo "You must install python3-venv before you can build the documentation."; exit 1; } + . $(DOCS_VENV); pip install $(PIPOPTS) --require-virtualenv \ + --upgrade -r requirements.txt \ + --log $(DOCS_VENVDIR)/pip_install.log + @test ! -f $(DOCS_VENVDIR)/pip_list.txt || \ + mv $(DOCS_VENVDIR)/pip_list.txt $(DOCS_VENVDIR)/pip_list.txt.bak + @. $(DOCS_VENV); pip list --local --format=freeze > $(DOCS_VENVDIR)/pip_list.txt + @touch $(DOCS_VENVDIR) + +pa11y-install: + @command -v $(PA11Y_CMD) >/dev/null || { \ + echo "Installing \"pa11y\" from npm..."; echo; \ + mkdir -p $(SPHINX_DIR)/node_modules/ ; \ + npm install --prefix $(SPHINX_DIR) pa11y; \ + } + +pymarkdownlnt-install: install + @. $(DOCS_VENV); test -d $(DOCS_VENVDIR)/lib/python*/site-packages/pymarkdown || pip install pymarkdownlnt==0.9.35 + +install: $(DOCS_VENVDIR) + +run: install + . $(DOCS_VENV); $(DOCS_VENVDIR)/bin/sphinx-autobuild -b dirhtml --host $(SPHINX_HOST) --port $(SPHINX_PORT) "$(DOCS_SOURCEDIR)" "$(DOCS_BUILDDIR)" $(SPHINX_OPTS) integrate: # Pull the other repositories @@ -75,52 +114,59 @@ integrate: # Override the MicroOVN tag with the circle of friends one (for consistency) cp .sphinx/_integration/tag.png integration/microovn/docs/.sphinx/_static/microovn.png -clean-integrate: - rm -rf integration/lxd - rm -rf integration/microceph - rm -rf integration/microovn - -clean: clean-integrate - $(MAKE) -f Makefile.sp sp-clean - # `html` builds the integrated docs. -html: integrate +html: integrate install mkdir -p $(current_dir)/_build cd integration/lxd/doc/ && $(MAKE) html BUILDDIR=$(current_dir)/_build/lxd cd integration/microceph/docs/ && $(MAKE) html BUILDDIR=$(current_dir)/_build/microceph cd integration/microovn/docs/ && $(MAKE) html BUILDDIR=$(current_dir)/_build/microovn - $(MAKE) -f Makefile.sp sp-html BUILDDIR=$(current_dir)/_build ADDPREREQS='gitpython pyyaml' + . $(DOCS_VENV); $(SPHINX_BUILD) -W --keep-going -b dirhtml "$(DOCS_SOURCEDIR)" "$(current_dir)/_build" -w $(SPHINX_DIR)/warnings.txt $(SPHINX_OPTS) # `html-rtd` builds the integrated docs, with the correct paths for Read the Docs. # This target is used by the Read the Docs build. -html-rtd: +html-rtd: install PATH_PREFIX=$(PATH_PREFIX) $(MAKE) -C integration/lxd/doc/ html-rtd BUILDDIR=$(READTHEDOCS_OUTPUT)/html/lxd PATH_PREFIX=$(PATH_PREFIX) $(MAKE) -C integration/microceph/docs/ html BUILDDIR=$(READTHEDOCS_OUTPUT)/html/microceph PATH_PREFIX=$(PATH_PREFIX) $(MAKE) -C integration/microovn/docs/ html BUILDDIR=$(READTHEDOCS_OUTPUT)/html/microovn - ADDPREREQS='gitpython pyyaml' PATH_PREFIX=$(PATH_PREFIX) $(MAKE) -f Makefile.sp sp-html BUILDDIR=$(READTHEDOCS_OUTPUT)/html + . $(DOCS_VENV); PATH_PREFIX=$(PATH_PREFIX) $(SPHINX_BUILD) -W --keep-going -b dirhtml "$(DOCS_SOURCEDIR)" "$(READTHEDOCS_OUTPUT)/html" -w $(SPHINX_DIR)/warnings.txt $(SPHINX_OPTS) -# `spelling` checks only the MicroCloud docs. -spelling: clean-doc microcloud - $(MAKE) -f Makefile.sp sp-spellcheck +epub: install + . $(DOCS_VENV); $(SPHINX_BUILD) -b epub "$(DOCS_SOURCEDIR)" "$(DOCS_BUILDDIR)" -w $(SPHINX_DIR)/warnings.txt $(SPHINX_OPTS) # `microcloud` builds only the MicroCloud docs, as a stand-alone doc set. -microcloud: - $(MAKE) -f Makefile.sp sp-html SINGLE_BUILD=True - -.PHONY: run -run: - $(MAKE) -f Makefile.sp sp-run LOCAL_SPHINX_BUILD=True ADDPREREQS='gitpython pyyaml' SPHINXOPTS="--ignore './index.html' -c . -d .sphinx/.doctrees -j auto" +microcloud: install + . $(DOCS_VENV); SINGLE_BUILD=True $(SPHINX_BUILD) -W --keep-going -b dirhtml "$(DOCS_SOURCEDIR)" "$(DOCS_BUILDDIR)" -w $(SPHINX_DIR)/warnings.txt $(SPHINX_OPTS) # `serve` rebuilds the integrated docs and serves them. serve: html - cd $(current_dir)/_build/; python3 -m http.server --bind 127.0.0.1 8000 + cd "$(DOCS_BUILDDIR)"; python3 -m http.server --bind $(SPHINX_HOST) $(SPHINX_PORT) -# `serve-microcloud` rebuilds the MicroCloud docs as a stand-along doc set and serves them. +# `serve-microcloud` rebuilds the MicroCloud docs as a stand-alone doc set and serves them. serve-microcloud: microcloud - cd $(current_dir)/_build/; python3 -m http.server --bind 127.0.0.1 8000 + cd "$(DOCS_BUILDDIR)"; python3 -m http.server --bind $(SPHINX_HOST) $(SPHINX_PORT) + +clean: clean-integrate clean-doc + @test ! -e "$(DOCS_VENVDIR)" -o -d "$(DOCS_VENVDIR)" -a "$(abspath $(DOCS_VENVDIR))" != "$(DOCS_VENVDIR)" + rm -rf $(DOCS_VENVDIR) + rm -rf $(SPHINX_DIR)/node_modules/ + rm -rf $(SPHINX_DIR)/styles + rm -rf $(SPHINX_DIR)/vale.ini + +clean-doc: + git clean -fx "$(DOCS_BUILDDIR)" + rm -rf $(SPHINX_DIR)/.doctrees + +clean-integrate: + rm -rf integration/lxd + rm -rf integration/microceph + rm -rf integration/microovn -pymarkdownlnt-install: install - @. $(DOCS_VENV); test -d $(DOCS_VENVDIR)/lib/python*/site-packages/pymarkdown || pip install pymarkdownlnt==0.9.35 +linkcheck: install + . $(DOCS_VENV) ; $(SPHINX_BUILD) -b linkcheck -q "$(DOCS_SOURCEDIR)" "$(DOCS_BUILDDIR)" $(SPHINX_OPTS) || { grep --color -F "[broken]" "$(DOCS_BUILDDIR)/output.txt"; exit 1; } + exit 0 + +pa11y: pa11y-install html + find $(DOCS_BUILDDIR) -name *.html -print0 | xargs -n 1 -0 $(PA11Y_CMD) # Without --return-code-scheme explicit, pymarkdownlnt returns 1 for multiple scenarios # By using the explicit scheme, it only returns 1 when no files are found, @@ -148,8 +194,32 @@ lint lint-md: pymarkdownlnt-install echo "Passed"; \ fi; -install: - $(MAKE) -f Makefile.sp sp-install ADDPREREQS='pyyaml gitpython' - + +woke: woke-install + woke $(ALLFILES) --exit-1-on-failure \ + -c https://github.com/canonical/Inclusive-naming/raw/main/config.yml + +woke-install: + @type woke >/dev/null 2>&1 || \ + { echo "Installing \"woke\" snap... \n"; sudo snap install woke; } + +vale: install + @. $(DOCS_VENV); test -d $(DOCS_VENVDIR)/lib/python*/site-packages/vale || pip install vale + @. $(DOCS_VENV); test -f $(SPHINX_DIR)/vale.ini || python3 $(SPHINX_DIR)/get_vale_conf.py + @. $(DOCS_VENV); find $(DOCS_VENVDIR)/lib/python*/site-packages/vale/vale_bin -size 195c -exec vale --config "$(SPHINX_DIR)/vale.ini" $(TARGET) > /dev/null \; + @echo "" + @echo "Running Vale against $(TARGET). To change target set TARGET= with make command" + @echo "" + @. $(DOCS_VENV); vale --config "$(SPHINX_DIR)/vale.ini" --glob='*.{md,txt,rst}' $(TARGET) +# `spelling` checks only the MicroCloud docs. +spelling: clean-doc microcloud spellcheck + +spellcheck: install + . $(DOCS_VENV); python3 -m pyspelling -c $(SPHINX_DIR)/spellingcheck.yaml -j $(shell nproc) + + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINX_OPTS). %: - $(MAKE) -f Makefile.sp sp-$@ + $(MAKE) --no-print-directory install + . $(DOCS_VENV); $(SPHINX_BUILD) -M $@ "$(DOCS_SOURCEDIR)" "$(DOCS_BUILDDIR)" $(SPHINX_OPTS) $(O) \ No newline at end of file diff --git a/doc/Makefile.sp b/doc/Makefile.sp deleted file mode 100644 index e258a8b66..000000000 --- a/doc/Makefile.sp +++ /dev/null @@ -1,109 +0,0 @@ -# Minimal makefile for Sphinx documentation -# -# `Makefile.sp` is from the Sphinx starter pack and should not be -# modified. -# Add your customisation to `Makefile` instead. - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXDIR = .sphinx -SPHINXOPTS ?= -c . -d $(SPHINXDIR)/.doctrees -j auto -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build -VENVDIR = $(SPHINXDIR)/venv -PA11Y = $(SPHINXDIR)/node_modules/pa11y/bin/pa11y.js --config $(SPHINXDIR)/pa11y.json -VENV = $(VENVDIR)/bin/activate -TARGET = * -ALLFILES = *.rst **/*.rst -ADDPREREQS ?= - -.PHONY: sp-full-help sp-woke-install sp-pa11y-install sp-install sp-run sp-html \ - sp-epub sp-serve sp-clean sp-clean-doc sp-spelling sp-spellcheck sp-linkcheck sp-woke \ - sp-pa11y Makefile.sp sp-vale - -sp-full-help: $(VENVDIR) - @. $(VENV); $(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - @echo "\n\033[1;31mNOTE: This help texts shows unsupported targets!\033[0m" - @echo "Run 'make help' to see supported targets." - -# If requirements are updated, venv should be rebuilt and timestamped. -$(VENVDIR): - @echo "... setting up virtualenv" - python3 -m venv $(VENVDIR) || { echo "You must install python3-venv before you can build the documentation."; exit 1; } - . $(VENV); pip install --require-virtualenv \ - --upgrade -r requirements.txt \ - --log $(VENVDIR)/pip_install.log - @test ! -f $(VENVDIR)/pip_list.txt || \ - mv $(VENVDIR)/pip_list.txt $(VENVDIR)/pip_list.txt.bak - @. $(VENV); pip list --local --format=freeze > $(VENVDIR)/pip_list.txt - @touch $(VENVDIR) - -sp-woke-install: - @type woke >/dev/null 2>&1 || \ - { echo "Installing \"woke\" snap... \n"; sudo snap install woke; } - -sp-pa11y-install: - @type $(PA11Y) >/dev/null 2>&1 || { \ - echo "Installing \"pa11y\" from npm... \n"; \ - mkdir -p $(SPHINXDIR)/node_modules/ ; \ - npm install --prefix $(SPHINXDIR) pa11y; \ - } - -sp-install: $(VENVDIR) - -sp-run: sp-install - . $(VENV); sphinx-autobuild -b dirhtml "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) - -# Doesn't depend on $(BUILDDIR) to rebuild properly at every run. -sp-html: sp-install - . $(VENV); $(SPHINXBUILD) -W --keep-going -b dirhtml "$(SOURCEDIR)" "$(BUILDDIR)" -w $(SPHINXDIR)/warnings.txt $(SPHINXOPTS) - -sp-epub: sp-install - . $(VENV); $(SPHINXBUILD) -b epub "$(SOURCEDIR)" "$(BUILDDIR)" -w $(SPHINXDIR)/warnings.txt $(SPHINXOPTS) - -sp-serve: sp-html - cd "$(BUILDDIR)"; python3 -m http.server --bind 127.0.0.1 8000 - -sp-clean: sp-clean-doc - @test ! -e "$(VENVDIR)" -o -d "$(VENVDIR)" -a "$(abspath $(VENVDIR))" != "$(VENVDIR)" - rm -rf $(VENVDIR) - rm -rf $(SPHINXDIR)/node_modules/ - rm -rf $(SPHINXDIR)/styles - rm -rf $(SPHINXDIR)/vale.ini - -sp-clean-doc: - git clean -fx "$(BUILDDIR)" - rm -rf $(SPHINXDIR)/.doctrees - -sp-spellcheck: - . $(VENV) ; python3 -m pyspelling -c $(SPHINXDIR)/spellingcheck.yaml -j $(shell nproc) - -sp-spelling: sp-html sp-spellcheck - -sp-linkcheck: sp-install - . $(VENV) ; $(SPHINXBUILD) -b linkcheck "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) || { grep --color -F "[broken]" "$(BUILDDIR)/output.txt"; exit 1; } - exit 0 - -sp-woke: sp-woke-install - woke $(ALLFILES) --exit-1-on-failure \ - -c https://github.com/canonical/Inclusive-naming/raw/main/config.yml - -sp-pa11y: sp-pa11y-install sp-html - find $(BUILDDIR) -name *.html -print0 | xargs -n 1 -0 $(PA11Y) - -sp-vale: sp-install - @. $(VENV); test -d $(SPHINXDIR)/venv/lib/python*/site-packages/vale || pip install vale - @. $(VENV); test -f $(SPHINXDIR)/vale.ini || python3 $(SPHINXDIR)/get_vale_conf.py - @. $(VENV); find $(SPHINXDIR)/venv/lib/python*/site-packages/vale/vale_bin -size 195c -exec vale --config "$(SPHINXDIR)/vale.ini" $(TARGET) > /dev/null \; - @echo "" - @echo "Running Vale against $(TARGET). To change target set TARGET= with make command" - @echo "" - @. $(VENV); vale --config "$(SPHINXDIR)/vale.ini" --glob='*.{md,txt,rst}' $(TARGET) - - - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile.sp - . $(VENV); $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/conf.py b/doc/conf.py index aa6e64efa..8bb129e47 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -188,6 +188,7 @@ 'Thumbs.db', '.DS_Store', '.sphinx', + '.venv', 'reference/release-notes/release-notes-template.md', "integration", 'README.md', From 713d554c1033d41556320ab203bed4044ae10878 Mon Sep 17 00:00:00 2001 From: Minae Lee Date: Mon, 4 May 2026 15:55:53 -0700 Subject: [PATCH 05/19] doc: replace pyspelling and woke with vale Signed-off-by: Minae Lee --- .wokeignore | 1 - doc/.gitignore | 2 - doc/.sphinx/get_vale_conf.py | 179 +++++++++++++++++++++++++++++---- doc/.sphinx/spellingcheck.yaml | 32 ------ doc/.wordlist.txt | 57 ----------- doc/Makefile | 72 +++++++------ doc/conf.py | 6 ++ doc/requirements.txt | 1 - 8 files changed, 206 insertions(+), 144 deletions(-) delete mode 120000 .wokeignore delete mode 100644 doc/.sphinx/spellingcheck.yaml delete mode 100644 doc/.wordlist.txt diff --git a/.wokeignore b/.wokeignore deleted file mode 120000 index 57c0c7b7e..000000000 --- a/.wokeignore +++ /dev/null @@ -1 +0,0 @@ -doc/.wokeignore \ No newline at end of file diff --git a/doc/.gitignore b/doc/.gitignore index 3fd1e898a..cffc0e345 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -1,7 +1,6 @@ /*env*/ .venv/ .sphinx/warnings.txt -.sphinx/.wordlist.dic .sphinx/.doctrees/ .sphinx/node_modules/ package*.json @@ -13,6 +12,5 @@ __pycache__ .sphinx/styles/* .sphinx/vale.ini integration/ - .sphinx/deps/ _static/swagger-ui \ No newline at end of file diff --git a/doc/.sphinx/get_vale_conf.py b/doc/.sphinx/get_vale_conf.py index 2f1c566f9..cebfa5a9d 100644 --- a/doc/.sphinx/get_vale_conf.py +++ b/doc/.sphinx/get_vale_conf.py @@ -1,29 +1,170 @@ -#! /usr/bin/env python +#!/usr/bin/env python3 -import requests import os +import shutil +import subprocess +import tempfile +import sys +import logging +import argparse -DIR=os.getcwd() +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' +) + +SPHINX_DIR = os.path.join(os.getcwd(), ".sphinx") + +GITHUB_REPO = "canonical/documentation-style-guide" +GITHUB_CLONE_URL = f"https://github.com/{GITHUB_REPO}.git" + +# Source paths to copy from repo +VALE_FILE_LIST = [ + "styles/Canonical", + "styles/config/vocabularies/Canonical", + "styles/config/dictionaries", + "vale.ini" +] + +def clone_repo_and_copy_paths(file_source_dest, overwrite=False): + """ + Clone the repository to a temporary directory and copy required files + + Args: + file_source_dest: dictionary of file paths to copy from the repository, + and their destination paths + overwrite: boolean flag to overwrite existing files in the destination + + Returns: + bool: Returns True only if all files were copied successfully. + Returns False if the input dictionary is empty, if the git clone fails, + or if any file fails to copy. + """ + + if not file_source_dest: + logging.error("No files to copy") + return False + + # Create temporary directory on disk for cloning + temp_dir = tempfile.mkdtemp() + logging.info("Cloning repository <%s> to temporary directory: %s", GITHUB_REPO, temp_dir) + clone_cmd = ["git", "clone", "--depth", "1", GITHUB_CLONE_URL, temp_dir] + + try: + result = subprocess.run( + clone_cmd, + capture_output=True, + text=True, + check=True + ) + logging.debug("Git clone output: %s", result.stdout) + + # Copy files from the cloned repository to the destination paths + is_copy_success = True + for source, dest in file_source_dest.items(): + source_path = os.path.join(temp_dir, source) + + if not copy_files_to_path(source_path, dest, overwrite): + is_copy_success = False + logging.error("Failed to copy %s to %s", source_path, dest) + + return is_copy_success + except subprocess.CalledProcessError as e: + logging.error("Git clone failed: %s", e.stderr) + return False + finally: + # Clean up temporary directory + logging.info("Cleaning up temporary directory: %s", temp_dir) + shutil.rmtree(temp_dir, ignore_errors=True) + +def copy_files_to_path(source_path, dest_path, overwrite=False): + """ + Copy a file or directory from source to destination + + Args: + source_path: Path to the source file or directory + dest_path: Path to the destination + overwrite: Boolean flag to overwrite existing files in the destination + + Returns: + bool: True if copy was successful, False otherwise + """ + # Skip if source file doesn't exist + if not os.path.exists(source_path): + logging.warning("Source path not found: %s", source_path) + return False + + logging.info("Copying %s to %s", source_path, dest_path) + # Handle existing files + if os.path.exists(dest_path): + if overwrite: + logging.info(" Destination exists, overwriting: %s", dest_path) + if os.path.isdir(dest_path): + shutil.rmtree(dest_path) + else: + os.remove(dest_path) + else: + logging.info(" Destination exists, skip copying (use overwrite=True to replace): %s", + dest_path) + return True # Skip copying + + # Create parent directory if it doesn't exist + parent_dir = os.path.dirname(dest_path) + if parent_dir: + os.makedirs(parent_dir, exist_ok=True) + + # Copy the source to destination + try: + if os.path.isdir(source_path): + # entire directory + shutil.copytree(source_path, dest_path) + else: + # individual files + shutil.copy2(source_path, dest_path) + return True + except (shutil.Error, OSError) as e: + logging.error("Copy failed: %s", e) + return False + +def parse_arguments(): + parser = argparse.ArgumentParser(description="Download Vale configuration files") + parser.add_argument("--no-overwrite", action="store_true", help="Don't overwrite existing files") + return parser.parse_args() def main(): + # Define local directory paths + vale_files_dict = {file: os.path.join(SPHINX_DIR, file) for file in VALE_FILE_LIST} + + # Parse command line arguments, default to overwrite_enabled = True + overwrite_enabled = not parse_arguments().no_overwrite + + # Clone repository to temporary directory and copy files to destination + if not clone_repo_and_copy_paths(vale_files_dict, overwrite=overwrite_enabled): + logging.error("Failed to download files from repository") + return 1 - if os.path.exists(f"{DIR}/.sphinx/styles/Canonical"): - print("Vale directory exists") - else: - os.makedirs(f"{DIR}/.sphinx/styles/Canonical") + # Replace the file type filter in vale.ini + vale_ini_path = os.path.join(SPHINX_DIR, "vale.ini") + try: + with open(vale_ini_path, 'r') as f: + content = f.read() + + # Replace the file type section + content = content.replace("[*.{md,txt,rst,html}]", "[*.{md}]") + + with open(vale_ini_path, 'w') as f: + f.write(content) + + logging.info("Updated vale.ini file type filter") + except (IOError, OSError) as e: + logging.error("Failed to update vale.ini: %s", e) + return 1 - url = "https://api.github.com/repos/canonical/praecepta/contents/styles/Canonical" - r = requests.get(url) - for item in r.json(): - download = requests.get(item["download_url"]) - file = open(".sphinx/styles/Canonical/" + item["name"], "w") - file.write(download.text) - file.close() + logging.info("Download complete") + return 0 - config = requests.get("https://raw.githubusercontent.com/canonical/praecepta/main/vale.ini") - file = open(".sphinx/vale.ini", "w") - file.write(config.text) - file.close() if __name__ == "__main__": - main() \ No newline at end of file + sys.exit(main()) # Keep return code \ No newline at end of file diff --git a/doc/.sphinx/spellingcheck.yaml b/doc/.sphinx/spellingcheck.yaml deleted file mode 100644 index f2f46553a..000000000 --- a/doc/.sphinx/spellingcheck.yaml +++ /dev/null @@ -1,32 +0,0 @@ -matrix: -- name: rST files - aspell: - lang: en - d: en_US - dictionary: - wordlists: - - .wordlist.txt - - .custom_wordlist.txt - output: .sphinx/.wordlist.dic - sources: - - _build/**/*.html - pipeline: - - pyspelling.filters.html: - comments: false - attributes: - - title - - alt - ignores: - - code - - pre - - spellexception - - .spellexception - - link - - title - - div.relatedlinks - - strong.command - - div.visually-hidden - - img - - a.p-navigation__link - - a.contributor - - a[title] diff --git a/doc/.wordlist.txt b/doc/.wordlist.txt deleted file mode 100644 index 7c0d2d077..000000000 --- a/doc/.wordlist.txt +++ /dev/null @@ -1,57 +0,0 @@ -# This wordlist is from the Sphinx starter pack and should not be -# modified. Add any custom terms to .custom_wordlist.txt instead. - -addons -API -APIs -balancer -Charmhub -CLI -Diátaxis -Dqlite -dropdown -EBS -EKS -enablement -favicon -Furo -Git -GitHub -Grafana -IAM -installable -JSON -Juju -Kubeflow -Kubernetes -Launchpad -linter -LTS -Makefile -Matrix -Mattermost -monmap -MyST -namespace -namespaces -NodePort -Numbat -observability -OEM -OIDC -OLM -Permalink -pre -Quickstart -ReadMe -reST -reStructuredText -RTD -subdirectories -subfolders -subtree -Ubuntu -UI -UUID -VM -YAML diff --git a/doc/Makefile b/doc/Makefile index 6f4359de0..f52386b25 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -15,10 +15,11 @@ DOCS_VENVDIR ?= .venv DOCS_VENV ?= $(DOCS_VENVDIR)/bin/activate DOCS_SOURCEDIR ?= . DOCS_BUILDDIR ?= _build - +DOCS_VOCAB ?= $(SPHINX_DIR)/styles/config/vocabularies/Canonical +VALE_DIR ?= $(DOCS_VENVDIR)/lib/python*/site-packages/vale +VALE_CONFIG ?= $(SPHINX_DIR)/vale.ini PA11Y_CMD ?= $(SPHINX_DIR)/node_modules/pa11y/bin/pa11y.js --config $(SPHINX_DIR)/pa11y.json -TARGET = * -ALLFILES = *.rst **/*.rst +CHECK_PATH ?= $(filter-out $(DOCS_VENVDIR),$(wildcard *)) INTEGRATION_DIR ?= integration # Put it first so that "make" without argument is like "make help". @@ -37,16 +38,16 @@ help: @echo "* check inclusive language: make woke" @echo "* check accessibility: make pa11y" @echo "* check style guide compliance: make vale" - @echo "* check style guide compliance on target: make vale TARGET=*" + @echo "* check style guide compliance on target: make vale CHECK_PATH=*" @echo "* other possible targets: make " @echo "-------------------------------------------------------------" @echo .PHONY: help full-help html epub linkcheck spelling spellcheck woke \ - vale pa11y run serve install pa11y-install \ - clean clean-doc lint-md lint \ - integrate clean-integrate html html-rtd microcloud \ - serve-microcloud woke-install pymarkdownlnt-install + vale pa11y run serve install pa11y-install \ + vale-install clean clean-doc lint-md lint \ + integrate clean-integrate html html-rtd microcloud \ + serve-microcloud pymarkdownlnt-install full-help: $(DOCS_VENVDIR) @. $(DOCS_VENV); $(SPHINX_BUILD) -M help "$(DOCS_SOURCEDIR)" "$(DOCS_BUILDDIR)" $(SPHINX_OPTS) $(O) @@ -150,7 +151,7 @@ clean: clean-integrate clean-doc rm -rf $(DOCS_VENVDIR) rm -rf $(SPHINX_DIR)/node_modules/ rm -rf $(SPHINX_DIR)/styles - rm -rf $(SPHINX_DIR)/vale.ini + rm -rf $(VALE_CONFIG) clean-doc: git clean -fx "$(DOCS_BUILDDIR)" @@ -194,29 +195,36 @@ lint lint-md: pymarkdownlnt-install echo "Passed"; \ fi; - -woke: woke-install - woke $(ALLFILES) --exit-1-on-failure \ - -c https://github.com/canonical/Inclusive-naming/raw/main/config.yml - -woke-install: - @type woke >/dev/null 2>&1 || \ - { echo "Installing \"woke\" snap... \n"; sudo snap install woke; } - -vale: install - @. $(DOCS_VENV); test -d $(DOCS_VENVDIR)/lib/python*/site-packages/vale || pip install vale - @. $(DOCS_VENV); test -f $(SPHINX_DIR)/vale.ini || python3 $(SPHINX_DIR)/get_vale_conf.py - @. $(DOCS_VENV); find $(DOCS_VENVDIR)/lib/python*/site-packages/vale/vale_bin -size 195c -exec vale --config "$(SPHINX_DIR)/vale.ini" $(TARGET) > /dev/null \; - @echo "" - @echo "Running Vale against $(TARGET). To change target set TARGET= with make command" - @echo "" - @. $(DOCS_VENV); vale --config "$(SPHINX_DIR)/vale.ini" --glob='*.{md,txt,rst}' $(TARGET) -# `spelling` checks only the MicroCloud docs. -spelling: clean-doc microcloud spellcheck - -spellcheck: install - . $(DOCS_VENV); python3 -m pyspelling -c $(SPHINX_DIR)/spellingcheck.yaml -j $(shell nproc) - +vale-install: install + @. $(DOCS_VENV); test -f $(VALE_CONFIG) || python3 $(SPHINX_DIR)/get_vale_conf.py + @echo '.Name=="Canonical.400-Enforce-inclusive-terms"' > $(SPHINX_DIR)/styles/woke.filter + @echo '.Level=="error" and .Name!="Canonical.500-Repeated-words" and .Name!="Canonical.000-US-spellcheck"' > $(SPHINX_DIR)/styles/error.filter + @echo '.Name=="Canonical.000-US-spellcheck"' > $(SPHINX_DIR)/styles/spelling.filter + @. $(DOCS_VENV); find $(VALE_DIR)/vale_bin -size 195c -exec vale --version \; + +woke: vale-install + @cat $(DOCS_VOCAB)/accept.txt > $(DOCS_VOCAB)/accept_backup.txt + @cat $(DOCS_SOURCEDIR)/.custom_wordlist.txt >> $(DOCS_VOCAB)/accept.txt + @echo "Running Vale acceptable term check against $(CHECK_PATH). To change target set CHECK_PATH= with make command" + @. $(DOCS_VENV); vale --config="$(VALE_CONFIG)" --filter='$(SPHINX_DIR)/styles/woke.filter' --glob='*.{md,rst}' $(CHECK_PATH) + @cat $(DOCS_VOCAB)/accept_backup.txt > $(DOCS_VOCAB)/accept.txt && rm $(DOCS_VOCAB)/accept_backup.txt + +vale: vale-install + @cat $(DOCS_VOCAB)/accept.txt > $(DOCS_VOCAB)/accept_backup.txt + @cat $(DOCS_SOURCEDIR)/.custom_wordlist.txt >> $(DOCS_VOCAB)/accept.txt + @echo "Running Vale against $(CHECK_PATH). To change target set CHECK_PATH= with make command" + @. $(DOCS_VENV); vale --config="$(VALE_CONFIG)" --filter='$(SPHINX_DIR)/styles/error.filter' --glob='*.{md,rst}' $(CHECK_PATH) + @cat $(DOCS_VOCAB)/accept_backup.txt > $(DOCS_VOCAB)/accept.txt && rm $(DOCS_VOCAB)/accept_backup.txt + +spelling: vale-install + @cat $(DOCS_VOCAB)/accept.txt > $(DOCS_VOCAB)/accept_backup.txt + @cat $(DOCS_SOURCEDIR)/.custom_wordlist.txt >> $(DOCS_VOCAB)/accept.txt + @echo "Running Vale against $(CHECK_PATH). To change target set CHECK_PATH= with make command" + @. $(DOCS_VENV); vale --config="$(VALE_CONFIG)" --filter='$(SPHINX_DIR)/styles/spelling.filter' --glob='*.{md,rst}' $(CHECK_PATH) + @cat $(DOCS_VOCAB)/accept_backup.txt > $(DOCS_VOCAB)/accept.txt && rm $(DOCS_VOCAB)/accept_backup.txt + +spellcheck: spelling + @echo "Please note that the \`make spellcheck\` command is being deprecated in favor of \`make spelling\`" # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINX_OPTS). diff --git a/doc/conf.py b/doc/conf.py index 8bb129e47..0b1f38f86 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -220,6 +220,12 @@ rst_prolog = ''' .. role:: center :class: align-center +.. role:: h2 + :class: hclass2 +.. role:: woke-ignore + :class: woke-ignore +.. role:: vale-ignore + :class: vale-ignore ''' # Load substitutions from YAML file diff --git a/doc/requirements.txt b/doc/requirements.txt index d73f292e4..06b6ec36f 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -21,7 +21,6 @@ sphinx-youtube-links~=0.1 # Other dependencies packaging~=26.1 -pyspelling sphinxcontrib-svg2pdfconverter[CairoSVG]~=2.1 sphinx-last-updated-by-git~=0.3 sphinx-sitemap~=2.9 From 64af5144ffad6e82d1f24703e1578e3e4c30bd1f Mon Sep 17 00:00:00 2001 From: Minae Lee Date: Mon, 4 May 2026 15:56:43 -0700 Subject: [PATCH 06/19] doc: fix vale-flagged spelling/woke issues Signed-off-by: Minae Lee --- doc/.custom_wordlist.txt | 1 + doc/explanation/security.md | 2 +- doc/how-to/member_remove.md | 2 +- doc/reference/release-notes/release-notes-template.md | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/.custom_wordlist.txt b/doc/.custom_wordlist.txt index 16850ab68..a5e678701 100644 --- a/doc/.custom_wordlist.txt +++ b/doc/.custom_wordlist.txt @@ -79,6 +79,7 @@ SDN snapd SSD SSL +subfolders subnet subnets SVG diff --git a/doc/explanation/security.md b/doc/explanation/security.md index f904720f0..8c4e5b451 100644 --- a/doc/explanation/security.md +++ b/doc/explanation/security.md @@ -31,7 +31,7 @@ Report potential security issues privately through GitHub by [filing a security MicroCloud manages cluster membership and encrypted communication through mTLS and certificate-based identities. When a machine joins a cluster, it verifies the cluster’s certificate fingerprint and receives the complete set of member certificates, establishing a consistent trust store. -During the join process, MicroCloud uses an **explicit trust establishment mechanism** designed to prevent secret leakage and mitigate man-in-the-middle attacks. This mechanism uses a Hash-Based Message Authentication Code (HMAC) to sign the messages exchanged between the machine that initiates the join process and the joining peers. The shared secret used for joining is never transmitted over the network. The join process also enforces rate limits and session timeouts to reduce the risk of replay and brute-force attacks. For further information, refer to the [public specification](https://discourse.ubuntu.com/t/explicit-trust-establishment-mechanism-for-microcloud/44261). +During the join process, MicroCloud uses an **explicit trust establishment mechanism** designed to prevent secret leakage and mitigate {spellexception}`man-in-the-middle` attacks. This mechanism uses a Hash-Based Message Authentication Code (HMAC) to sign the messages exchanged between the machine that initiates the join process and the joining peers. The shared secret used for joining is never transmitted over the network. The join process also enforces rate limits and session timeouts to reduce the risk of replay and brute-force attacks. For further information, refer to the [public specification](https://discourse.ubuntu.com/t/explicit-trust-establishment-mechanism-for-microcloud/44261). (exp-security-lxd)= ## LXD diff --git a/doc/how-to/member_remove.md b/doc/how-to/member_remove.md index e72071744..499bc2833 100644 --- a/doc/how-to/member_remove.md +++ b/doc/how-to/member_remove.md @@ -33,7 +33,7 @@ Removing a cluster member with `--force` will not attempt to perform any clean-u ## Reducing the cluster to one member -When shrinking the cluster down to one member, you must also clean up the Ceph monmap before proceeding, even when using the `--force` flag. +When shrinking the cluster down to one member, you must also clean up the Ceph monitor map (`monmap`) before proceeding, even when using the `--force` flag. ```bash sudo microceph.ceph mon remove diff --git a/doc/reference/release-notes/release-notes-template.md b/doc/reference/release-notes/release-notes-template.md index 22156ce04..a84f807f1 100644 --- a/doc/reference/release-notes/release-notes-template.md +++ b/doc/reference/release-notes/release-notes-template.md @@ -9,7 +9,7 @@ myst: % NOTE: This is a basic template for MicroCloud release notes. It is excluded from the Sphinx build of the documentation site. -% Uncomment one of the two sentences below, and delete the other. +% Remove the comment prefix (%) from one of the two sentences below, and delete the other. % This is a {ref}`feature release ` and is not recommended for production use. % This is a {ref}`LTS release ` and is recommended for production use. From 5c765a178f2ee9b2884964102c0074673440cbdd Mon Sep 17 00:00:00 2001 From: Minae Lee Date: Mon, 4 May 2026 19:08:43 -0700 Subject: [PATCH 07/19] doc: use canonical-sphinx theme with customizations Replaces use of the canonical-sphinx-extensions extension with the canonical-sphinx extension, adapted for MicroCloud customizations. The canonical-sphinx-extensions package included multiple extensions that have now been split out into their own extensions and updated, including sphinx-terminal, and these have been re-added as needed. Signed-off-by: Minae Lee --- doc/.sphinx/_static/404.svg | 13 - doc/.sphinx/_static/custom.css | 378 ------------------ doc/.sphinx/_static/footer.css | 47 --- doc/.sphinx/_static/footer.js | 12 - doc/.sphinx/_static/furo_colors.css | 89 ----- doc/.sphinx/_static/github_issue_links.css | 24 -- doc/.sphinx/_static/github_issue_links.js | 34 -- doc/.sphinx/_static/header-nav.js | 10 - doc/.sphinx/_static/header.css | 167 -------- .../_static/{tag.png => microcloud_tag.png} | Bin doc/.sphinx/_templates/404.html | 17 - doc/.sphinx/_templates/footer.html | 76 +--- doc/.sphinx/_templates/header.html | 42 +- doc/.sphinx/_templates/page.html | 49 --- doc/Makefile | 4 +- doc/conf.py | 55 +-- doc/requirements.txt | 4 +- 17 files changed, 72 insertions(+), 949 deletions(-) delete mode 100644 doc/.sphinx/_static/404.svg delete mode 100644 doc/.sphinx/_static/custom.css delete mode 100644 doc/.sphinx/_static/footer.css delete mode 100644 doc/.sphinx/_static/footer.js delete mode 100644 doc/.sphinx/_static/furo_colors.css delete mode 100644 doc/.sphinx/_static/github_issue_links.css delete mode 100644 doc/.sphinx/_static/github_issue_links.js delete mode 100644 doc/.sphinx/_static/header-nav.js delete mode 100644 doc/.sphinx/_static/header.css rename doc/.sphinx/_static/{tag.png => microcloud_tag.png} (100%) delete mode 100644 doc/.sphinx/_templates/404.html delete mode 100644 doc/.sphinx/_templates/page.html diff --git a/doc/.sphinx/_static/404.svg b/doc/.sphinx/_static/404.svg deleted file mode 100644 index b353cd339..000000000 --- a/doc/.sphinx/_static/404.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/doc/.sphinx/_static/custom.css b/doc/.sphinx/_static/custom.css deleted file mode 100644 index 3ceeb188e..000000000 --- a/doc/.sphinx/_static/custom.css +++ /dev/null @@ -1,378 +0,0 @@ -/** - Ubuntu variable font definitions. - Based on https://github.com/canonical/vanilla-framework/blob/main/scss/_base_fontfaces.scss - - When font files are updated in Vanilla, the links to font files will need to be updated here as well. -*/ - -/* default font set */ -@font-face { - font-family: 'Ubuntu variable'; - font-stretch: 100%; /* min and max value for the width axis, expressed as percentage */ - font-style: normal; - font-weight: 100 800; /* min and max value for the weight axis */ - src: url('https://assets.ubuntu.com/v1/f1ea362b-Ubuntu%5Bwdth,wght%5D-latin-v0.896a.woff2') format('woff2-variations'); -} - -@font-face { - font-family: 'Ubuntu variable'; - font-stretch: 100%; /* min and max value for the width axis, expressed as percentage */ - font-style: italic; - font-weight: 100 800; /* min and max value for the weight axis */ - src: url('https://assets.ubuntu.com/v1/90b59210-Ubuntu-Italic%5Bwdth,wght%5D-latin-v0.896a.woff2') format('woff2-variations'); -} - -@font-face { - font-family: 'Ubuntu Mono variable'; - font-style: normal; - font-weight: 100 800; /* min and max value for the weight axis */ - src: url('https://assets.ubuntu.com/v1/d5fc1819-UbuntuMono%5Bwght%5D-latin-v0.869.woff2') format('woff2-variations'); -} - -/* cyrillic-ext */ -@font-face { - font-family: 'Ubuntu variable'; - font-stretch: 100%; /* min and max value for the width axis, expressed as percentage */ - font-style: normal; - font-weight: 100 800; /* min and max value for the weight axis */ - src: url('https://assets.ubuntu.com/v1/77cd6650-Ubuntu%5Bwdth,wght%5D-cyrillic-extended-v0.896a.woff2') format('woff2-variations'); - unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F; -} - -/* cyrillic */ -@font-face { - font-family: 'Ubuntu variable'; - font-stretch: 100%; /* min and max value for the width axis, expressed as percentage */ - font-style: normal; - font-weight: 100 800; /* min and max value for the weight axis */ - src: url('https://assets.ubuntu.com/v1/2702fce5-Ubuntu%5Bwdth,wght%5D-cyrillic-v0.896a.woff2') format('woff2-variations'); - unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} - -/* greek-ext */ -@font-face { - font-family: 'Ubuntu variable'; - font-stretch: 100%; /* min and max value for the width axis, expressed as percentage */ - font-style: normal; - font-weight: 100 800; /* min and max value for the weight axis */ - src: url('https://assets.ubuntu.com/v1/5c108b7d-Ubuntu%5Bwdth,wght%5D-greek-extended-v0.896a.woff2') format('woff2-variations'); - unicode-range: U+1F00-1FFF; -} - -/* greek */ -@font-face { - font-family: 'Ubuntu variable'; - font-stretch: 100%; /* min and max value for the width axis, expressed as percentage */ - font-style: normal; - font-weight: 100 800; /* min and max value for the weight axis */ - src: url('https://assets.ubuntu.com/v1/0a14c405-Ubuntu%5Bwdth,wght%5D-greek-v0.896a.woff2') format('woff2-variations'); - unicode-range: U+0370-03FF; -} - -/* latin-ext */ -@font-face { - font-family: 'Ubuntu variable'; - font-stretch: 100%; /* min and max value for the width axis, expressed as percentage */ - font-style: normal; - font-weight: 100 800; /* min and max value for the weight axis */ - src: url('https://assets.ubuntu.com/v1/19f68eeb-Ubuntu%5Bwdth,wght%5D-latin-extended-v0.896a.woff2') format('woff2-variations'); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; -} - - -/** Define font-weights as per Vanilla - Based on: https://github.com/canonical/vanilla-framework/blob/main/scss/_base_typography-definitions.scss - - regular text: 400, - bold: 550, - thin: 300, - - h1: bold, - h2: 180; - h3: bold, - h4: 275, - h5: bold, - h6: regular -*/ - -/* default regular text */ -html { - font-weight: 400; -} - -/* heading specific definitions */ -h1, h3, h5 { font-weight: 550; } -h2 { font-weight: 180; } -h4 { font-weight: 275; } - -/* bold */ -.toc-tree li.scroll-current>.reference, -dl.glossary dt, -dl.simple dt, -dl:not([class]) dt { - font-weight: 550; -} - - -/** Table styling **/ - -th.head { - text-transform: uppercase; - font-size: var(--font-size--small); - text-align: initial; -} - -table.align-center th.head { - text-align: center -} - -table.docutils { - border: 0; - box-shadow: none; - width:100%; -} - -table.docutils td, table.docutils th, table.docutils td:last-child, table.docutils th:last-child, table.docutils td:first-child, table.docutils th:first-child { - border-right: none; - border-left: none; -} - -/* Allow to centre text horizontally in table data cells */ -table.align-center { - text-align: center !important; -} - -/** No rounded corners **/ - -.admonition, code.literal, .sphinx-tabs-tab, .sphinx-tabs-panel, .highlight { - border-radius: 0; -} - -/** Admonition styling **/ - -.admonition { - border-top: 1px solid #d9d9d9; - border-right: 1px solid #d9d9d9; - border-bottom: 1px solid #d9d9d9; -} - -/** Color for the "copy link" symbol next to headings **/ - -a.headerlink { - color: var(--color-brand-primary); -} - -/** Line to the left of the current navigation entry **/ - -.sidebar-tree li.current-page { - border-left: 2px solid var(--color-brand-primary); -} - -/** Some tweaks for Sphinx tabs **/ - -[role="tablist"] { - border-bottom: 1px solid var(--color-sidebar-item-background--hover); -} - -.sphinx-tabs-tab[aria-selected="true"], .sd-tab-set>input:checked+label{ - border: 0; - border-bottom: 2px solid var(--color-brand-primary); - font-weight: 400; - font-size: 1rem; - color: var(--color-brand-primary); -} - -body[data-theme="dark"] .sphinx-tabs-tab[aria-selected="true"] { - background: var(--color-background-primary); - border-bottom: 2px solid var(--color-brand-primary); -} - -button.sphinx-tabs-tab[aria-selected="false"]:hover, .sd-tab-set>input:not(:checked)+label:hover { - border-bottom: 2px solid var(--color-foreground-border); -} - -button.sphinx-tabs-tab[aria-selected="false"]{ - border-bottom: 2px solid var(--color-background-primary); -} - -body[data-theme="dark"] .sphinx-tabs-tab { - background: var(--color-background-primary); -} - -.sphinx-tabs-tab, .sd-tab-set>label{ - color: var(--color-brand-primary); - font-family: var(--font-stack); - font-weight: 400; - font-size: 1rem; - padding: 1em 1.25em .5em -} - -.sphinx-tabs-panel { - border: 0; - border-bottom: 1px solid var(--color-sidebar-item-background--hover); - background: var(--color-background-primary); - padding: 0.75rem 0 0.75rem 0; -} - -body[data-theme="dark"] .sphinx-tabs-panel { - background: var(--color-background-primary); -} - -/** A tweak for issue #190 **/ - -.highlight .hll { - background-color: var(--color-highlighted-background); -} - - -/** Custom classes to fix scrolling in tables by decreasing the - font size or breaking certain columns. - Specify the classes in the Markdown file with, for example: - ```{rst-class} break-col-4 min-width-4-8 - ``` -**/ - -table.dec-font-size { - font-size: smaller; -} -table.break-col-1 td.text-left:first-child { - word-break: break-word; -} -table.break-col-4 td.text-left:nth-child(4) { - word-break: break-word; -} -table.min-width-1-15 td.text-left:first-child { - min-width: 15em; -} -table.min-width-4-8 td.text-left:nth-child(4) { - min-width: 8em; -} - -/** Underline for abbreviations **/ - -abbr[title] { - text-decoration: underline solid #cdcdcd; -} - -/** Use the same style for right-details as for left-details **/ -.bottom-of-page .right-details { - font-size: var(--font-size--small); - display: block; -} - -/** Version switcher */ -button.version_select { - color: var(--color-foreground-primary); - background-color: var(--color-toc-background); - padding: 5px 10px; - border: none; -} - -.version_select:hover, .version_select:focus { - background-color: var(--color-sidebar-item-background--hover); -} - -.version_dropdown { - position: relative; - display: inline-block; - text-align: right; - font-size: var(--sidebar-item-font-size); -} - -.available_versions { - display: none; - position: absolute; - right: 0px; - background-color: var(--color-toc-background); - box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); - z-index: 11; -} - -.available_versions a { - color: var(--color-foreground-primary); - padding: 12px 16px; - text-decoration: none; - display: block; -} - -.available_versions a:hover {background-color: var(--color-sidebar-item-background--current)} - -/** Suppress link underlines outside on-hover **/ -a { - text-decoration: none; -} - -a:hover, a:visited:hover { - text-decoration: underline; -} - -.show {display:block;} - -/** Fix for nested numbered list - the nested list is lettered **/ -ol.arabic ol.arabic { - list-style: lower-alpha; -} - -/** Make expandable sections look like links **/ -details summary { - color: var(--color-link); -} - -/** Fix the styling of the version box for readthedocs **/ - -#furo-readthedocs-versions .rst-versions, #furo-readthedocs-versions .rst-current-version, #furo-readthedocs-versions:focus-within .rst-current-version, #furo-readthedocs-versions:hover .rst-current-version { - background: var(--color-sidebar-item-background--hover); -} - -.rst-versions .rst-other-versions dd a { - color: var(--color-link); -} - -#furo-readthedocs-versions:focus-within .rst-current-version .fa-book, #furo-readthedocs-versions:hover .rst-current-version .fa-book, .rst-versions .rst-other-versions { - color: var(--color-sidebar-link-text); -} - -.rst-versions .rst-current-version { - color: var(--color-version-popup); - font-weight: bolder; -} - -/* Code-block copybutton invisible by default - (overriding Furo config to achieve default copybutton setting). */ -.highlight button.copybtn { - opacity: 0; -} - -/* Mimicking the 'Give feedback' button for UX consistency */ -.sidebar-search-container input[type=submit] { - color: #FFFFFF; - border: 2px solid #D6410D; - padding: var(--sidebar-search-input-spacing-vertical) var(--sidebar-search-input-spacing-horizontal); - background: #D6410D; - font-weight: bold; - font-size: var(--font-size--small); - cursor: pointer; -} - -.sidebar-search-container input[type=submit]:hover { - text-decoration: underline; -} - -/* Make inline code the same size as code blocks */ -p code.literal { - border: 0; - font-size: var(--code-font-size); -} - -/* Use the general admonition font size for inline code */ -.admonition p code.literal { - font-size: var(--admonition-font-size); -} - -.highlight .s, .highlight .s1, .highlight .s2 { - color: #3F8100; -} - -.highlight .o { - color: #BB5400; -} diff --git a/doc/.sphinx/_static/footer.css b/doc/.sphinx/_static/footer.css deleted file mode 100644 index a0a1db454..000000000 --- a/doc/.sphinx/_static/footer.css +++ /dev/null @@ -1,47 +0,0 @@ -.display-contributors { - color: var(--color-sidebar-link-text); - cursor: pointer; -} -.all-contributors { - display: none; - z-index: 55; - list-style: none; - position: fixed; - top: 0; - bottom: 0; - left: 0; - right: 0; - width: 200px; - height: 200px; - overflow-y: scroll; - margin: auto; - padding: 0; - background: var(--color-background-primary); - scrollbar-color: var(--color-foreground-border) transparent; - scrollbar-width: thin; -} - -.all-contributors li:hover { - background: var(--color-sidebar-item-background--hover); - width: 100%; -} - -.all-contributors li a{ - color: var(--color-sidebar-link-text); - padding: 1rem; - display: inline-block; -} - -#overlay { - position: fixed; - display: none; - width: 100%; - height: 100%; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0,0,0,0.5); - z-index: 2; - cursor: pointer; -} diff --git a/doc/.sphinx/_static/footer.js b/doc/.sphinx/_static/footer.js deleted file mode 100644 index 9a08b1e99..000000000 --- a/doc/.sphinx/_static/footer.js +++ /dev/null @@ -1,12 +0,0 @@ -$(document).ready(function() { - $(document).on("click", function () { - $(".all-contributors").hide(); - $("#overlay").hide(); - }); - - $('.display-contributors').click(function(event) { - $('.all-contributors').toggle(); - $("#overlay").toggle(); - event.stopPropagation(); - }); -}) diff --git a/doc/.sphinx/_static/furo_colors.css b/doc/.sphinx/_static/furo_colors.css deleted file mode 100644 index 4cfdbe7bf..000000000 --- a/doc/.sphinx/_static/furo_colors.css +++ /dev/null @@ -1,89 +0,0 @@ -body { - --color-code-background: #f8f8f8; - --color-code-foreground: black; - --code-font-size: 1rem; - --font-stack: Ubuntu variable, Ubuntu, -apple-system, Segoe UI, Roboto, Oxygen, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; - --font-stack--monospace: Ubuntu Mono variable, Ubuntu Mono, Consolas, Monaco, Courier, monospace; - --color-foreground-primary: #111; - --color-foreground-secondary: var(--color-foreground-primary); - --color-foreground-muted: #333; - --color-background-secondary: #FFF; - --color-background-hover: #f2f2f2; - --color-brand-primary: #111; - --color-brand-content: #06C; - --color-api-background: #E3E3E3; - --color-inline-code-background: rgba(0,0,0,.03); - --color-sidebar-link-text: #111; - --color-sidebar-item-background--current: #ebebeb; - --color-sidebar-item-background--hover: #f2f2f2; - --toc-font-size: var(--font-size--small); - --color-admonition-title-background--note: var(--color-background-primary); - --color-admonition-title-background--tip: var(--color-background-primary); - --color-admonition-title-background--important: var(--color-background-primary); - --color-admonition-title-background--caution: var(--color-background-primary); - --color-admonition-title--note: #24598F; - --color-admonition-title--tip: #24598F; - --color-admonition-title--important: #C7162B; - --color-admonition-title--caution: #F99B11; - --color-highlighted-background: #EBEBEB; - --color-link-underline: var(--color-link); - --color-link-underline--hover: var(--color-link); - --color-link-underline--visited: var(--color-link--visited); - --color-link-underline--visited--hover: var(--color-link--visited); - --color-version-popup: #772953; -} - -@media not print { - body[data-theme="dark"] { - --color-code-background: #202020; - --color-code-foreground: #d0d0d0; - --color-foreground-secondary: var(--color-foreground-primary); - --color-foreground-muted: #CDCDCD; - --color-background-secondary: var(--color-background-primary); - --color-background-hover: #666; - --color-brand-primary: #fff; - --color-brand-content: #69C; - --color-sidebar-link-text: #f7f7f7; - --color-sidebar-item-background--current: #666; - --color-sidebar-item-background--hover: #333; - --color-admonition-background: transparent; - --color-admonition-title-background--note: var(--color-background-primary); - --color-admonition-title-background--tip: var(--color-background-primary); - --color-admonition-title-background--important: var(--color-background-primary); - --color-admonition-title-background--caution: var(--color-background-primary); - --color-admonition-title--note: #24598F; - --color-admonition-title--tip: #24598F; - --color-admonition-title--important: #C7162B; - --color-admonition-title--caution: #F99B11; - --color-highlighted-background: #666; - --color-version-popup: #F29879; - } - @media (prefers-color-scheme: dark) { - body:not([data-theme="light"]) { - --color-api-background: #A4A4A4; - --color-code-background: #202020; - --color-code-foreground: #d0d0d0; - --color-foreground-secondary: var(--color-foreground-primary); - --color-foreground-muted: #CDCDCD; - --color-background-secondary: var(--color-background-primary); - --color-background-hover: #666; - --color-brand-primary: #fff; - --color-brand-content: #69C; - --color-sidebar-link-text: #f7f7f7; - --color-sidebar-item-background--current: #666; - --color-sidebar-item-background--hover: #333; - --color-admonition-background: transparent; - --color-admonition-title-background--note: var(--color-background-primary); - --color-admonition-title-background--tip: var(--color-background-primary); - --color-admonition-title-background--important: var(--color-background-primary); - --color-admonition-title-background--caution: var(--color-background-primary); - --color-admonition-title--note: #24598F; - --color-admonition-title--tip: #24598F; - --color-admonition-title--important: #C7162B; - --color-admonition-title--caution: #F99B11; - --color-highlighted-background: #666; - --color-link: #F9FCFF; - --color-version-popup: #F29879; - } - } -} diff --git a/doc/.sphinx/_static/github_issue_links.css b/doc/.sphinx/_static/github_issue_links.css deleted file mode 100644 index db166ed95..000000000 --- a/doc/.sphinx/_static/github_issue_links.css +++ /dev/null @@ -1,24 +0,0 @@ -.github-issue-link-container { - padding-right: 0.5rem; -} -.github-issue-link { - font-size: var(--font-size--small); - font-weight: bold; - background-color: #D6410D; - padding: 13px 23px; - text-decoration: none; -} -.github-issue-link:link { - color: #FFFFFF; -} -.github-issue-link:visited { - color: #FFFFFF -} -.muted-link.github-issue-link:hover { - color: #FFFFFF; - text-decoration: underline; -} -.github-issue-link:active { - color: #FFFFFF; - text-decoration: underline; -} diff --git a/doc/.sphinx/_static/github_issue_links.js b/doc/.sphinx/_static/github_issue_links.js deleted file mode 100644 index f0706038b..000000000 --- a/doc/.sphinx/_static/github_issue_links.js +++ /dev/null @@ -1,34 +0,0 @@ -// if we already have an onload function, save that one -var prev_handler = window.onload; - -window.onload = function() { - // call the previous onload function - if (prev_handler) { - prev_handler(); - } - - const link = document.createElement("a"); - link.classList.add("muted-link"); - link.classList.add("github-issue-link"); - link.text = "Give feedback"; - link.href = ( - github_url - + "/issues/new?" - + "title=docs%3A+TYPE+YOUR+QUESTION+HERE" - + "&body=*Please describe the question or issue you're facing with " - + `"${document.title}"` - + ".*" - + "%0A%0A%0A%0A%0A" - + "---" - + "%0A" - + `*Reported+from%3A+${location.href}*` - ); - link.target = "_blank"; - - const div = document.createElement("div"); - div.classList.add("github-issue-link-container"); - div.append(link) - - const container = document.querySelector(".article-container > .content-icon-container"); - container.prepend(div); -}; diff --git a/doc/.sphinx/_static/header-nav.js b/doc/.sphinx/_static/header-nav.js deleted file mode 100644 index 3608576e0..000000000 --- a/doc/.sphinx/_static/header-nav.js +++ /dev/null @@ -1,10 +0,0 @@ -$(document).ready(function() { - $(document).on("click", function () { - $(".more-links-dropdown").hide(); - }); - - $('.nav-more-links').click(function(event) { - $('.more-links-dropdown').toggle(); - event.stopPropagation(); - }); -}) diff --git a/doc/.sphinx/_static/header.css b/doc/.sphinx/_static/header.css deleted file mode 100644 index 0b9440903..000000000 --- a/doc/.sphinx/_static/header.css +++ /dev/null @@ -1,167 +0,0 @@ -.p-navigation { - border-bottom: 1px solid var(--color-sidebar-background-border); -} - -.p-navigation__nav { - background: #333333; - display: flex; -} - -.p-logo { - display: flex !important; - padding-top: 0 !important; - text-decoration: none; -} - -.p-logo-image { - height: 44px; - padding-right: 10px; -} - -.p-logo-text { - margin-top: 18px; - color: white; - text-decoration: none; -} - -ul.p-navigation__links { - display: flex; - list-style: none; - margin-left: 0; - margin-top: auto; - margin-bottom: auto; - max-width: 800px; - width: 100%; -} - -ul.p-navigation__links li { - margin: 0 auto; - text-align: center; - width: 100%; -} - -ul.p-navigation__links li a { - background-color: rgba(0, 0, 0, 0); - border: none; - border-radius: 0; - color: var(--color-sidebar-link-text); - display: block; - font-weight: 400; - line-height: 1.5rem; - margin: 0; - overflow: hidden; - padding: 1rem 0; - position: relative; - text-align: left; - text-overflow: ellipsis; - transition-duration: .1s; - transition-property: background-color, color, opacity; - transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); - white-space: nowrap; - width: 100%; -} - -ul.p-navigation__links .p-navigation__link { - color: #ffffff; - font-weight: 300; - text-align: center; - text-decoration: none; -} - -ul.p-navigation__links .p-navigation__link:hover { - background-color: #2b2b2b; -} - -ul.p-navigation__links .p-dropdown__link:hover { - background-color: var(--color-sidebar-item-background--hover); -} - -ul.p-navigation__links .p-navigation__sub-link { - background: var(--color-background-primary); - padding: .5rem 0 .5rem .5rem; - font-weight: 300; -} - -ul.p-navigation__links .more-links-dropdown li a { - border-left: 1px solid var(--color-sidebar-background-border); - border-right: 1px solid var(--color-sidebar-background-border); -} - -ul.p-navigation__links .more-links-dropdown li:first-child a { - border-top: 1px solid var(--color-sidebar-background-border); -} - -ul.p-navigation__links .more-links-dropdown li:last-child a { - border-bottom: 1px solid var(--color-sidebar-background-border); -} - -ul.p-navigation__links .p-navigation__logo { - padding: 0.5rem; -} - -ul.p-navigation__links .p-navigation__logo img { - width: 40px; -} - -ul.more-links-dropdown { - display: none; - overflow-x: visible; - height: 0; - z-index: 55; - padding: 0; - position: relative; - list-style: none; - margin-bottom: 0; - margin-top: 0; -} - -.nav-more-links::after { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath fill='%23111' d='M8.187 11.748l6.187-6.187-1.06-1.061-5.127 5.127L3.061 4.5 2 5.561z'/%3E%3C/svg%3E"); - background-position: center; - background-repeat: no-repeat; - background-size: contain; - content: ""; - display: block; - filter: invert(100%); - height: 1rem; - pointer-events: none; - position: absolute; - right: 1rem; - text-indent: calc(100% + 10rem); - top: calc(1rem + 0.25rem); - width: 1rem; -} - -.nav-ubuntu-com { - display: none; -} - -@media only screen and (min-width: 480px) { - ul.p-navigation__links li { - width: 100%; - } - - .nav-ubuntu-com { - display: inherit; - } -} - -@media only screen and (max-width: 800px) { - .nav-more-links { - margin-left: auto !important; - padding-right: 2rem !important; - width: 8rem !important; - } -} - -@media only screen and (min-width: 800px) { - ul.p-navigation__links li { - width: 100% !important; - } -} - -@media only screen and (min-width: 1310px) { - ul.p-navigation__links { - margin-left: calc(50% - 41em); - } -} diff --git a/doc/.sphinx/_static/tag.png b/doc/.sphinx/_static/microcloud_tag.png similarity index 100% rename from doc/.sphinx/_static/tag.png rename to doc/.sphinx/_static/microcloud_tag.png diff --git a/doc/.sphinx/_templates/404.html b/doc/.sphinx/_templates/404.html deleted file mode 100644 index 4cb2d50d3..000000000 --- a/doc/.sphinx/_templates/404.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "page.html" %} - -{% block content -%} -
-

Page not found

-
-
-
- {{ body }} -
-
- Penguin with a question mark -
-
-
-
-{%- endblock content %} diff --git a/doc/.sphinx/_templates/footer.html b/doc/.sphinx/_templates/footer.html index 577478c95..242f91ab3 100644 --- a/doc/.sphinx/_templates/footer.html +++ b/doc/.sphinx/_templates/footer.html @@ -5,7 +5,7 @@ {% if meta %} {% if 'sequential_nav' in meta %} {% set sequential_nav = meta.sequential_nav %} - {% endif %} + {% endif %} {% endif %} {# mod: Conditional wrappers to control page navigation buttons #} {% if sequential_nav != "none" -%} @@ -43,15 +43,26 @@ {%- endif %} + {%- if license and license.name -%} + {%- if license.url -%} +
+ This page is licensed under {{ license.name }} +
+ {%- else -%} +
+ This page is licensed under {{ license.name }} +
+ {%- endif -%} + {%- endif -%} {# mod: removed "Made with" #} @@ -70,65 +81,8 @@ {%- endif %} -
- {% if github_url and github_folder and pagename and page_source_suffix and display_contributors %} - {% set contributors = get_contribs(github_url, github_folder, pagename, page_source_suffix, display_contributors_since) %} - {% if contributors %} - {% if contributors | length > 1 %} - Thanks to the {{ contributors |length }} contributors! - {% else %} - Thanks to our contributor! - {% endif %} -
- - {% endif %} - {% endif %} -
- - {# mod: replaced RTD icons with our links #} - - {% if discourse %} - - {% endif %} - - {% if mattermost %} - - {% endif %} - - {% if matrix %} - - {% endif %} - - {% if github_url and github_version and github_folder %} - - {% if github_issues %} - - {% endif %} - - - {% endif %} - {# mod: Added link to manage cookie tracker settings #} - - -
+ diff --git a/doc/.sphinx/_templates/header.html b/doc/.sphinx/_templates/header.html index bf2cb23e8..60508aa21 100644 --- a/doc/.sphinx/_templates/header.html +++ b/doc/.sphinx/_templates/header.html @@ -7,28 +7,58 @@