From 69e0940862a4e5fd6fbe5b6712dfdbc079ef8b78 Mon Sep 17 00:00:00 2001 From: Pedro Brochado Date: Wed, 29 May 2024 17:30:04 -0300 Subject: [PATCH 1/2] Add mkdocs-literate-nav to handle ordering and recursive navs Closes: #20 --- pyproject.toml | 1 + src/pulp_docs/data/mkdocs.yml | 2 + src/pulp_docs/navigation.py | 112 ++++------------- src/pulp_docs/utils/aggregation.py | 186 ++++++++++------------------- 4 files changed, 94 insertions(+), 207 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 73e78b5..c269d8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ dependencies = [ "mkdocstrings-python>=1.9.1", "mkdocs-macros-plugin", "mkdocs-site-urls", + "mkdocs-literate-nav", "importlib_resources", "httpx", "rich", diff --git a/src/pulp_docs/data/mkdocs.yml b/src/pulp_docs/data/mkdocs.yml index 7bcdbbd..13e80aa 100644 --- a/src/pulp_docs/data/mkdocs.yml +++ b/src/pulp_docs/data/mkdocs.yml @@ -45,6 +45,8 @@ plugins: module_name: '../mkdocs_macros' - tags: tags_file: pulp-docs/docs/tags.md + - literate-nav: + nav_file: _SUMMARY.md - mkdocstrings: handlers: python: diff --git a/src/pulp_docs/navigation.py b/src/pulp_docs/navigation.py index c0b62c6..652adff 100644 --- a/src/pulp_docs/navigation.py +++ b/src/pulp_docs/navigation.py @@ -57,93 +57,31 @@ def grouped_by_persona(tmpdir: Path, repos: Repos): {content-type} """ f = AgregationUtils(tmpdir, repos) - usage_section = [ - {"Overview": f.section_file("user/index.md")}, - { - "Pulpcore": [ - f.section( - Names.PULPCORE_TUTORIAL, - f.get_children, - "pulpcore/docs/user/tutorials", - ), - f.section(Names.LEARN, f.get_children, "pulpcore/docs/user/learn"), - f.section(Names.GUIDES, f.get_children, "pulpcore/docs/user/guides"), - ] - }, - { - "Plugins": f.repo_grouping( - "{repo}/docs/user/{content}", repo_types=["content"] - ) - }, - {"Extras": f.repo_grouping("{repo}/docs/user/{content}", repo_types=["other"])}, - ] - admin_section = [ - {"Overview": f.section_file("admin/index.md")}, - { - "Pulpcore": [ - f.section( - Names.PULPCORE_TUTORIAL, - f.get_children, - "pulpcore/docs/admin/tutorials", - ), - f.section(Names.LEARN, f.get_children, "pulpcore/docs/admin/learn"), - f.section(Names.GUIDES, f.get_children, "pulpcore/docs/admin/guides"), - ] - }, - f.section( - "Plugins", - f.repo_grouping, - "{repo}/docs/admin/{content}", - repo_types=["content"], - hide_empty_section=False, - ), - f.section( - "Extras", - f.repo_grouping, - "{repo}/docs/admin/{content}", - repo_types=["other"], - hide_empty_section=False, - ), - ] - development_section = [ - {"Overview": f.section_file("dev/index.md")}, - { - "Pulpcore": [ - f.section( - Names.PULPCORE_TUTORIAL, - f.get_children, - "pulpcore/docs/dev/tutorials", - ), - f.section(Names.LEARN, f.get_children, "pulpcore/docs/dev/learn"), - f.section(Names.GUIDES, f.get_children, "pulpcore/docs/dev/guides"), - ] - }, - { - "Plugins": f.repo_grouping( - "{repo}/docs/dev/{content}", repo_types=["content"] - ) - }, - {"Extras": f.repo_grouping("{repo}/docs/dev/{content}", repo_types=["other"])}, - ] + SECTION_HOST = "pulp-docs" + CHANGES_PATH = "{repo}/changes/changelog.md" + + # Manual section for each persona + manual_nav = {} + for section in ("user", "admin", "dev"): + TEMPLATE_STRING = "{repo}/docs/%s/{content}" % section + section_nav = [ + {"Overview": f"{SECTION_HOST}/docs/sections/{section}/index.md"}, + {"Core": f.repo_grouping(TEMPLATE_STRING, repo_types=["core"])}, + {"Plugins": f.repo_grouping(TEMPLATE_STRING, repo_types=["content"])}, + {"Extras": f.repo_grouping(TEMPLATE_STRING, repo_types=["other"])}, + ] + manual_nav[section] = section_nav + + # Custom help section help_section = [ - {"Overview": f.section_file("help/index.md")}, - {"Community": f.get_children("pulp-docs/docs/sections/help/community")}, - {"More": f.get_children("pulp-docs/docs/sections/help/more")}, + {"Overview": f"{SECTION_HOST}/docs/sections/help/index.md"}, + {"Community": f"{SECTION_HOST}/docs/sections/help/community/"}, + {"More": f"{SECTION_HOST}/docs/sections/help/more/"}, { "Changelogs": [ - {"Pulpcore": "pulpcore/changes/changelog.md"}, - { - "Plugins": sorted( - f.repo_grouping( - "{repo}/changes", repo_types=["content"] - ).items() - ) - }, - { - "Extra": sorted( - f.repo_grouping("{repo}/changes", repo_types=["other"]).items() - ) - }, + {"Core": f.changes_grouping(CHANGES_PATH, repo_types=["core"])}, + {"Plugins": f.changes_grouping(CHANGES_PATH, repo_types=["content"])}, + {"Extra": f.changes_grouping(CHANGES_PATH, repo_types=["other"])}, ] }, ] @@ -151,9 +89,9 @@ def grouped_by_persona(tmpdir: Path, repos: Repos): # Main Section navigation = [ {"Home": "index.md"}, - {"User Manual": usage_section}, - {"Admin Manual": admin_section}, - {"Developer Manual": development_section}, + {"User Manual": manual_nav["user"]}, + {"Admin Manual": manual_nav["admin"]}, + {"Developer Manual": manual_nav["dev"]}, {"Help": help_section}, ] return navigation diff --git a/src/pulp_docs/utils/aggregation.py b/src/pulp_docs/utils/aggregation.py index c9f8f4c..2d30bfa 100644 --- a/src/pulp_docs/utils/aggregation.py +++ b/src/pulp_docs/utils/aggregation.py @@ -27,33 +27,6 @@ def section(self, name: str, fn: t.Callable, *args, **kwargs) -> dict: return {name: section} return {name: section} if section else {"": ""} - def get_children(self, path: t.Union[str, Path]) -> t.List[str]: - """ - Get all markdown files contained in @path recursively. - - Uses the dirname.title() as the subsection name if there are subdirs. - """ - basepath = self.tmpdir / path if isinstance(path, str) else path - if not basepath.exists(): - return [] - - def _get_tree(_path): - """Recursive scandir""" - with os.scandir(_path) as it: - children = [] - for entry in sorted(it, key=lambda x: x.name): - if entry.is_file() and entry.name.endswith(".md"): - filename = str(Path(entry.path).relative_to(self.tmpdir)) - children.append(filename) - elif entry.is_dir(): - dir_title = self.normalize_title(entry.name) - sub_section = {dir_title: _get_tree(entry)} - children.append(sub_section) - return children - - result = _get_tree(basepath) - return result - def normalize_title(self, raw_title: str): return raw_title.replace("_", " ").title() @@ -89,57 +62,80 @@ def repo_grouping( } ``` """ - _nav = {} - _expand_content_types = "{content}" in template_str - # Selected a set of repos - selected_repos = [] selected_content = content_types or [ "tutorials", "guides", "learn", "reference", ] - if not repo_types: # default case - selected_repos = self.repos.all - else: - selected_repos.extend(self.repos.get_repos(repo_types=repo_types)) - - # Dont expand content-types - if not _expand_content_types: - for repo in selected_repos: - lookup_path = self._parse_template_str(template_str, repo.name) - _repo_content = self.get_children(lookup_path) + + selected_repos = self.repos.all + if repo_types: + selected_repos = self.repos.get_repos(repo_types=repo_types) + + group_nav = [] + for repo in selected_repos: + repo_nav = [] + # Include index.md if present in staging_docs/{persona}/index.md + persona_basepath = self._parse_template_str( + template_str, repo.name, "placeholder" + ).parent + index_path = persona_basepath / "index.md" + if index_path.exists(): + repo_nav.append({"Overview": str(index_path.relative_to(self.tmpdir))}) + + for content_type in selected_content: + # Get repo files from content-type and persona + lookup_path = self._parse_template_str( + template_str, repo.name, content_type + ) + _repo_content = self._add_literate_nav_dir(lookup_path) + + # Prevent rendering content-type section if there are no files if _repo_content: - _nav[repo.title] = ( - _repo_content if len(_repo_content) > 1 else _repo_content[0] - ) - # Expand content-types - else: - for repo in selected_repos: - repo_nav = {} - # Include index.md if present in staging_docs/{persona}/index.md - persona_basepath = self._parse_template_str( - template_str, repo.name, "placeholder" - ).parent - index_path = persona_basepath / "index.md" - if index_path.exists(): - repo_nav["Overview"] = str(index_path.relative_to(self.tmpdir)) - - for content_type in selected_content: - # Get repo files from content-type and persona - lookup_path = self._parse_template_str( - template_str, repo.name, content_type - ) - _repo_content = self.get_children(lookup_path) - - # Prevent rendering content-type section if there are no files - if _repo_content: - content_type_name = Names.get(content_type) - repo_nav[content_type_name] = _repo_content # type: ignore - if repo_nav: - _nav[repo.title] = repo_nav - return _nav or ["#"] + content_type_title = Names.get(content_type) + repo_nav.append({content_type_title: _repo_content}) # type: ignore + if repo_nav: + group_nav.append({repo.title: repo_nav}) + return group_nav or ["#"] + + def changes_grouping( + self, changes_path_template: str, repo_types: t.Optional[t.List[str]] = None + ): + selected_repos = self.repos.all + if repo_types: + selected_repos = self.repos.get_repos(repo_types=repo_types) + + group_nav = [ + {repo.title: changes_path_template.format(repo=repo.name)} + for repo in selected_repos + ] + return group_nav or ["#"] # type: ignore + + def _add_literate_nav_dir(self, lookup_path: Path) -> t.Optional[str]: + """ + Take a path and return a path-str or None. + + The path-str is expanded by mkdocs-literate-nav. E.g: + + For "foo/bar/eggs/", it will: + 1. Try to find "foo/bar/eggs/_SUMMARY.md" + 2. If cant find, generate recursive nav for "foo/bar/eggs" + + If the @lookup_path is non-existent, non-dir or doesnt contain .md, returns None. + """ + if not lookup_path.exists(): + return None + + if not lookup_path.is_dir(): + return None + + if len(list(lookup_path.rglob("*.md"))) == 0: + return None + + path_str = str(lookup_path.relative_to(self.tmpdir)) + "/" + return path_str def _parse_template_str( self, template_str: str, repo_name: str, content_type: t.Optional[str] = None @@ -163,53 +159,3 @@ def _parse_template_str( return self.tmpdir / template_str.format(**kwargs) - def _pop_quickstart_from(self, pathlist: t.List[str]) -> t.Optional[str]: - """Get quickstart.md file from filelist with case and variations tolerance""" - for path in pathlist: - if not isinstance(path, str): - continue - - filename = path.split("/")[-1] - if filename.lower() in ("quickstart.md", "quick-start.md"): - pathlist.remove(path) - return path - return None - - def repo_reference_grouping(self): - """ - Create reference section by aggregating some specific files. - - Group according to the pattern: - {repo}/ - Readme.md - Rest Api.md - Code Api/ - Module A.md - Module B.md - Changelog.md - - """ - template_str = "{repo}/docs/reference" - _nav = {} - for repo in self.repos.all: - lookup_path = self.tmpdir / template_str.format(repo=repo.name) - _repo_content = self.get_children(lookup_path) - reference_section = [ - {"REST API": f"{repo.name}/docs/rest_api.md"}, - {"Readme": f"{repo.name}/README.md"}, - {"Code API": _repo_content}, - {"Changelog": f"{repo.name}/CHANGELOG.md"}, - ] - _nav[repo.title] = reference_section - return _nav - - def section_file(self, section_and_filename: str): - """Get a markdown file from the website section folder.""" - basepath = "pulp-docs/docs/sections" - return f"{basepath}/{section_and_filename}" - - def section_children(self, section_name: str): - """Get children markdown files from the website section folder.""" - basepath = "pulpcore/docs/sections" - section = self.get_children(f"{basepath}/{section_name}") - return section From 5209461b62b7002b3f8c10c647180abad5924a3d Mon Sep 17 00:00:00 2001 From: Pedro Brochado Date: Wed, 29 May 2024 18:11:47 -0300 Subject: [PATCH 2/2] Add documentation for ordering and improve cheatsheet style --- .../dev/reference/markdown-cheatsheet.md | 189 ++++++++---------- 1 file changed, 87 insertions(+), 102 deletions(-) diff --git a/staging_docs/dev/reference/markdown-cheatsheet.md b/staging_docs/dev/reference/markdown-cheatsheet.md index d21eed6..fa034c0 100644 --- a/staging_docs/dev/reference/markdown-cheatsheet.md +++ b/staging_docs/dev/reference/markdown-cheatsheet.md @@ -4,147 +4,132 @@ There are basic markdown features recommended for use in Pulp. ### Internal links -=== "Template" +Adding absolute links is enabled by `mkdocs-site-urls` +(learn more [here](https://github.com/octoprint/mkdocs-site-urls)). +This is preferred over *relative* links because of our complex structure. See tradeoffs [here](https://github.com/pulp/pulp-docs/issues/2) - ```markdown - [My Link](site:{repo-name}/docs/{persona}/{content-type}/some-page.md). - ``` - -=== "Sample" +```markdown +# Template +[My Link](site:{repo-name}/docs/{persona}/{content-type}/some-page.md). - ```markdown - [My Link](site:pulp-docs/docs/dev/reference/markdown-cheatsheet.md). - ``` +# Example +[My Link](site:pulp-docs/docs/dev/reference/markdown-cheatsheet.md). -=== "Sample Rendered" +# Rendered +[My Link](http://127.0.0.1:8000/pulp-docs/docs/dev/reference/markdown-cheatsheet/). +``` - ```markdown - [My Link](http://127.0.0.1:8000/pulp-docs/docs/dev/reference/markdown-cheatsheet/). - ``` +### Tabbed content -- This is enabled by [mkdocs-site-urls](https://github.com/octoprint/mkdocs-site-urls) plugin. -- This is preferred over *relative* links because of our complex structure. See tradeoffs [here](https://github.com/pulp/pulp-docs/issues/2) +Having tabs for alternative content is enabled by mkdocs-material +(learn more [here](https://squidfunk.github.io/mkdocs-material/reference/content-tabs/#usage)). +It looks like this: +!!! example -### Codeblocks + === "Run" + ```bash + cat myfile.txt + ``` -=== "Raw" + === "Output" - ```` - ``` - - proxypass /pulp/api http://${pulp-api}/pulp/api - proxypassreverse /pulp/api http://${pulp-api}/pulp/api - requestheader set foo "asdf" - - ``` - ```` + ```bash + line1 + line2 + line3 + ``` - --- +The basic syntax is: - ``` - - proxypass /pulp/api http://${pulp-api}/pulp/api - proxypassreverse /pulp/api http://${pulp-api}/pulp/api - requestheader set foo "asdf" - - ``` - -=== "Python Language" - - ```` - ```python - serializer = mymodelserializer(data=data) - serializer.is_valid(raise_exception=true) - instance = serializer.create(serializer.validated_data) - ``` - ```` +- Each tab title must be in quotes +- Each tab block must have 4 spaces of indent +- Put space around `=== "Title"` - --- +```` title="sample-tabbed-content.md" +=== "Run" - ```python - serializer = mymodelserializer(data=data) - serializer.is_valid(raise_exception=true) - instance = serializer.create(serializer.validated_data) + ```bash + cat myfile.txt ``` -=== "Python REPL" +=== "Output" - ```` - ```python - >>> serializer = mymodelserializer(data=data) - >>> serializer.is_valid(raise_exception=true) - >>> instance = serializer.create(serializer.validated_data) - Some output + ```bash + line1 + line2 + line3 ``` - ```` +```` - --- - ```python - >>> serializer = mymodelserializer(data=data) - >>> serializer.is_valid(raise_exception=true) - >>> instance = serializer.create(serializer.validated_data) - Some output - ``` +### Ordering Files -=== "With 'Title'" +Ordering is enabled by `mkdocs-literate-nav` +(learn more [here](https://oprypin.github.io/mkdocs-literate-nav/reference.html)). - ```` - ```bash title="script.sh" - pulp file repository update --name myrepo --retained-versions 1 - ``` - ```` +The basic setup is having a a file in `{repo}/docs/{user}/{persona}/_SUMMARY.md`. +In this file you should use the following syntax: - --- +```markdown title="pulp_rpm/docs/user/guides/_SUMMARY.md" +# Basic +* [First list item](z-page.md) +* [Second list item](m-page.md) +* [Third list item](a-page.md) - ```bash title="script.sh" - pulp file repository update --name myrepo --retained-versions 1 - ``` +# Subdirs +* [First list item](some-page.md) +* Subsection title + * [Foo](subdirectory/foo.md) + * [Bar](subdirectory/bar.md) -### Tabbed content +# Recursive Globs +* [I want this first](some-page.md) +* *.md +* [This last and recurse this dir](other/) +``` +### Codeblocks -
+Mkdocs-material comes with builtin support for syntax highlighting in codeblocks. +You may use `python`, `bash`, `yaml`, `json`, `yaml` and most popular programming languages. -```` -=== "Run" +!!! example - ```bash - cat myfile.txt + ```python + serializer = mymodelserializer(data=data) + serializer.is_valid(raise_exception=true) + instance = serializer.create(serializer.validated_data) + print(instance) ``` -=== "Output" +Markdown syntax: - ```bash - line1 - line2 - line3 - ``` +```` +```python +serializer = mymodelserializer(data=data) +serializer.is_valid(raise_exception=true) +instance = serializer.create(serializer.validated_data) +``` ```` -=== "Run" +The codeblock can also have a title, which is useful if its a file: - ```bash - cat myfile.txt - ``` +!!! example -=== "Output" - - ```bash - line1 - line2 - line3 + ```bash title="sample-title-here.sh" + pulp file repository update --name myrepo --retained-versions 1 ``` -
- -- [See mkdocs-material](https://squidfunk.github.io/mkdocs-material/reference/content-tabs/#usage) -- Each tab title must be in quotes -- Each tab block must have 4 spaces of indent -- Put space around `=== "Title"` +Markdown syntax: +```` +```bash title="script.sh" +pulp file repository update --name myrepo --retained-versions 1 +``` +```` + ### Admonitions [See mkdocs-material](https://squidfunk.github.io/mkdocs-material/reference/admonitions/#supported-types)